让VC++直接生成汇编代码

时间:2023-02-01 11:44:09

看了李子明写的那篇《让汇编揭开死循环的神秘面纱》后,突然有个想法,那就是能不能把C++的代码直接翻译成ASM的,那样对于我们程序分析就更加清析了,也不用反汇编去分析了。在网上没看到相关类似的教程,于是,便有了此文。
首先,我们来看看子明兄原文中的小程序:

#include <stdio.h>

void main()
{
int j, b[10];
for ( j = 0; j <= 10; j++ )
{
b[j] = 0;
}
}

请问这个程序是否有错?A.正常 B.越界 C.死循环

我想很多朋友会选择B,当然不排除有选择A和C的,其实正确答案是C,子明兄在文章中用逆向工程把原理已经说得很清楚了,这里就不再累述了。

从子明兄文章里我们可以总结出逆向工程具体的流程大概如下:
1、 高级语言程序(C、C++、...)-->编译-->生成可执行程序(机器码/汇编语言)
2、可执行程序-->反编译(IDA、OD、WIN32DASM等反编译软件)-->汇编代码
3、汇编代码-->分析、了解汇编代码程序流程-->进行逆向分析或者实现某个功能
4、逆向工程完毕

在我们没有程序源代码的情况下,做逆向分析也只有按照上述的流程(各位大大有什么更好的方法还希望分享下),然后我们在有源代码的情况,是不是可以直接来生成汇编代码而省去反编译那一步呢?答案是肯定的。
我们先把程序做如下改变(看汇编代码的时候更清楚):

/*
Author : 独孤依人(CNSST)
Date : 2007-10-27
Content: 代码演示让VC++直接生成汇编代码程序示例
*/
#include <stdio.h>

void main()
{
int j=0, b[10]={1,2,3,4,5,6,7,8,9,10};
for ( j = 0; j <= 10; j++ )
{
b[j] = 0;
}

}

接下来,我们通过如下几步设置:
1) 选择菜单工程(Project)设置(Settings),出现如图1所示界面: 图1
让VC++直接生成汇编代码
2) 选择C/C++标签,然后在分类(Category)中选择Listing Files,然后在列表文件类型(Listing file type)选择Assembly with Source code,如果图2所示: 图2
让VC++直接生成汇编代码 这样设置之后就可以输出汇编代码了。
3) 编译构件执行后,我们看到Debug目录已生成了对应的汇编代码,如图3所示: 图3
让VC++直接生成汇编代码

下面,我对生成的汇编代码做简单的解释(CNSST注://……为我加上的解释):
TITLE C:/Documents and Settings/Administrator/桌面/test/test.cpp //程序名
.386P //编译成386保护模式指令
include listing.inc
if @Version gt 510 //判断汇编语言编译器版本是否大于5.1
.model FLAT //以FLAT内存模式编译
Else //以下声明一些段,之后对应C的相关代码,程序生成时都有相关注释。
_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
$$SYMBOLS SEGMENT BYTE USE32 'DEBSYM'
$$SYMBOLS ENDS
$$TYPES SEGMENT BYTE USE32 'DEBTYP'
$$TYPES ENDS
_TLS SEGMENT DWORD USE32 PUBLIC 'TLS'
_TLS ENDS
; COMDAT _main
_TEXT SEGMENT PARA USE32 PUBLIC 'CODE'
_TEXT ENDS
FLAT GROUP _DATA, CONST, _BSS //所有的段共享FLAT组
ASSUME CS: FLAT, DS: FLAT, SS: FLAT
endif
PUBLIC _main
; COMDAT _main
_TEXT SEGMENT
_i$ = -4
_b$ = -44
_main PROC NEAR ; COMDAT

; 4 : {

push ebp
mov ebp, esp
sub esp, 108 ; 0000006cH
push ebx
push esi
push edi
lea edi, DWORD PTR [ebp-108]
mov ecx, 27 ; 0000001bH
mov eax, -858993460 ; ccccccccH
rep stosd

; 5 : int j=0, b[10]={1,2,3,4,5,6,7,8,9,10};

mov DWORD PTR _i$[ebp], 0
mov DWORD PTR _b$[ebp], 1
mov DWORD PTR _b$[ebp+4], 2
mov DWORD PTR _b$[ebp+8], 3
mov DWORD PTR _b$[ebp+12], 4
mov DWORD PTR _b$[ebp+16], 5
mov DWORD PTR _b$[ebp+20], 6
mov DWORD PTR _b$[ebp+24], 7
mov DWORD PTR _b$[ebp+28], 8
mov DWORD PTR _b$[ebp+32], 9
mov DWORD PTR _b$[ebp+36], 10 ; 0000000aH

; 6 : for ( ij= 0; j <= 10; j++ )

mov DWORD PTR _i$[ebp], 0
jmp SHORT $L529
$L530:
mov eax, DWORD PTR _i$[ebp]
add eax, 1
mov DWORD PTR _i$[ebp], eax
$L529:
cmp DWORD PTR _i$[ebp], 10 ; 0000000aH
jg SHORT $L531

; 8 : b[j] = 0;

mov ecx, DWORD PTR _i$[ebp]
mov DWORD PTR _b$[ebp+ecx*4], 0

; 9 : }

jmp SHORT $L530
$L531:

; 10 : }

pop edi
pop esi
pop ebx
mov esp, ebp
pop ebp
ret 0
_main ENDP
_TEXT ENDS
END

直接生成的汇编代码看起来比反汇编的直观多了,再来分析其中的原理,挑出其中的一段代码如下:

; 5 : int j=0, b[10]={1,2,3,4,5,6,7,8,9,10};

mov DWORD PTR _i$[ebp], 0
mov DWORD PTR _b$[ebp], 1
mov DWORD PTR _b$[ebp+4], 2
mov DWORD PTR _b$[ebp+8], 3
mov DWORD PTR _b$[ebp+12], 4
mov DWORD PTR _b$[ebp+16], 5
mov DWORD PTR _b$[ebp+20], 6
mov DWORD PTR _b$[ebp+24], 7
mov DWORD PTR _b$[ebp+28], 8
mov DWORD PTR _b$[ebp+32], 9
mov DWORD PTR _b$[ebp+36], 10 ; 0000000aH

; 6 : for ( j = 0; j <= 10; j++ )

mov DWORD PTR _i$[ebp], 0
jmp SHORT $L529
这段代码让迷团浮出水面,具体分析见子明兄原文,这里也不再累述。此文到此算是结束了,对逆向工程也许没有什么用,但对于学习逆向工程的朋友或许有些帮助。本文方法只是一个小技巧而已,除此之外,还可以通过命令行程序CL.exe来做到,这里也不再累述。