首页
论坛
专栏
课程

[原创]编译器如何读取源文件

2011-10-15 04:13 11761

[原创]编译器如何读取源文件

2011-10-15 04:13
11761
闲来无事,随便逆逆。
首先编译器编译的时候会使用CraeteFileW创建源文件,这个断点我们可以OD打开一个编译器,直接对所有文件操作的函数下断就能断下来了。先看第一个,创建源文件:
0044F680  /$  53            PUSH EBX
0044F681  |.  8B4424 08     MOV EAX,DWORD PTR SS:[ESP+8]
0044F685  |.  8B5C24 0C     MOV EBX,DWORD PTR SS:[ESP+C]
0044F689  |.  6A 00         PUSH 0                                             ; /hTemplateFile = NULL
0044F68B  |.  6A 00         PUSH 0                                             ; |Attributes = 0
0044F68D  |.  6A 03         PUSH 3                                             ; |Mode = OPEN_EXISTING
0044F68F  |.  6A 00         PUSH 0                                             ; |pSecurity = NULL
0044F691  |.  6A 01         PUSH 1                                             ; |ShareMode = FILE_SHARE_READ
0044F693  |.  68 00000080   PUSH 80000000                                      ; |Access = GENERIC_READ
0044F698  |.  50            PUSH EAX                                           ; |目标源文件
0044F699  |.  FF15 4CD05100 CALL DWORD PTR DS:[<&KERNEL32.CreateFileW>]        ; \CreateFileW
0044F69F  |.  8903          MOV DWORD PTR DS:[EBX],EAX                         ;  返回句柄
0044F6A1  |.  833B FF       CMP DWORD PTR DS:[EBX],-1                          ;  判断是否为空
0044F6A4  |.  75 08         JNZ SHORT 0044F6AE
0044F6A6  |.  FF15 18CF5100 CALL DWORD PTR DS:[<&KERNEL32.GetLastError>]       ; [GetLastError
0044F6AC  |.  EB 02         JMP SHORT 0044F6B0

然后使用先前分配的内存,把源代码文件映射到内存,这个内存大小分配可能是解析自己格式文件的出来的,比如vc有.dsw。
CreateFile之后就ReadFile读取前4字节,前4字节一般就是#inc,然后比较这4字节是否等于0xFFFEFEFF,这个字符我们弄到十六进制编辑器里面会发现是空白,
0x0FEFF

之后循重复比较读取字节的长度是否为3,2长度,比较是否为空白,不是就设置文件指针指向文件开始。再然后清除读取的源代码,GetFileSizeEx获得源代码的大小,
00450168  |.  50            PUSH EAX                                           ; /Arg1
00450169  |.  E8 82F7FFFF   CALL 0044F8F0                                      ; \再次获得文件大小
0045016E  |.  85D2          TEST EDX,EDX
00450170  |.  72 10         JB SHORT 00450182
00450172  |.  77 07         JA SHORT 0045017B
00450174  |.  3D 00000080   CMP EAX,80000000                                   ;  原代码大小
00450179  |.  76 07         JBE SHORT 00450182                                 ;  小于等于这个数就跳
0045017B  |>  B8 05000020   MOV EAX,20000005                                   ;  不然设置EAX为这个数字,后返回
00450180  |.  EB 57         JMP SHORT 004501D9

之后就是使用读取PE文件那两个函数映射源代码文件到内存。
读取之后判断是否为UTF8编码,相关代码如下:
004B2A6B  |.  56            PUSH ESI                                           ;  原代码大小
004B2A6C  |.  8B45 E4       MOV EAX,DWORD PTR SS:[EBP-1C]
004B2A6F  |.  50            PUSH EAX                                           ;  指向原代码内存的指针
004B2A70  |.  FF15 F0D65100 CALL DWORD PTR DS:[<&support._IsEncodingUTF8>]     ;  support._IsEncodingUTF8

然后从源文件开始查找第一个换行或者回车。查找到之后就用MultiByteToWideChar转换为宽字符,期间分配了另外的内存空间。
004B2DDA  |> /46            /INC ESI                                           ;  ESI指向内存中的源代码字符,
004B2DDB  |> |3B75 FC        CMP ESI,DWORD PTR SS:[EBP-4]
004B2DDE  |. |73 16         |JNB SHORT 004B2DF6
004B2DE0  |. |8A06          |MOV AL,BYTE PTR DS:[ESI]
004B2DE2  |. |3C 0A         |CMP AL,0A
004B2DE4  |. |74 10         |JE SHORT 004B2DF6                                 ;  比较是否为换行,查找第一个换行或者第一个回车
004B2DE6  |. |3C 0D         |CMP AL,0D
004B2DE8  |. |74 0C         |JE SHORT 004B2DF6                                 ;  比较是否为回车
004B2DEA  |. |89F0          |MOV EAX,ESI
004B2DEC  |. |2B45 F8       |SUB EAX,DWORD PTR SS:[EBP-8]
004B2DEF  |. |3D 00100000   |CMP EAX,1000
004B2DF4  |.^\7C E4         \JL SHORT 004B2DDA
004B2DF6  |>  3B75 F8       CMP ESI,DWORD PTR SS:[EBP-8]                       ;  查找到第一个换行或者回车之后就跳

映射之后
004B2E04  |.  803E 0D       CMP BYTE PTR DS:[ESI],0D                           ;  判断是否为回车
004B2E07  |.  75 0F         JNZ SHORT 004B2E18
004B2E09  |.  807E 01 0A    CMP BYTE PTR DS:[ESI+1],0A                         ;  换行
004B2E0D  |.  75 09         JNZ SHORT 004B2E18
004B2E0F  |.  834B 18 0C    OR DWORD PTR DS:[EBX+18],0C
004B2E13  |.  83C6 02       ADD ESI,2                                          ;  是就字符+2

然后在宽字符内存空间的后面添加一个换行符。
之后继续循环比较,从换行和回车后面开始比较。
这个过程就是检查头文件语法。因为#include <windows>这后面是回车换行,
上面这个过程转换为宽字符,其中还有些处理。剩下的。。。。

[公告]安全测试和项目外包请将项目需求发到看雪企服平台:https://qifu.kanxue.com

最新回复 (18)
格鲁 2011-10-15 10:36
2
0
既然是编译器,那为什么你搞的都是这些细枝末节?词法分析呢
爱鸟 1 2011-10-15 13:33
3
0
看了半天 不懂什么叫语法映射
yarpee 1 2011-10-15 14:57
4
0
没有具体的编译过程
邓韬 9 2011-10-15 21:54
5
0
核心程序在ml.EXE里面呢。
邓韬 9 2011-10-15 22:10
6
0
我的理解就是:编译器从源文件读取代码,到内存中检查源码的各个结构,然后发现哪行不对,就显示错误
exile 1 2011-10-15 22:20
7
0
这都什么东西啊。
没本 2011-10-16 08:59
8
0
要研究编译器,有开源的gcc和clang不去读,逆这个有意义吗。
邓韬 9 2011-10-16 09:03
9
0
为什么那么肯定我是研究编译器啊?说不定我不是想研究编译器呢?
bitt 5 2011-10-16 16:10
10
0
feff是unicode big endian fffe是unicode little endian判断头两字节是在分辨编码方式
yeweijun 2011-10-16 16:35
11
0
CreateFile之后就ReadFile读取前4字节,前4字节一般就是#inc,然后比较这4字节是否等于0xFFFEFEFF,这个字符我们弄到十六进制编辑器里面会发现是空白

汗。。。注释怎么办。晕。
没本 2011-10-17 10:31
12
0
明白了,你这是source code embedded virus的可行性分析。
bbbsl 2011-10-17 15:16
13
0
标题起的太大了,不如改成简单谈谈vc是如何读取源文件的
zapline 2011-10-18 12:30
14
0
FFFE是UTF8
cntrump 13 2011-10-18 12:42
15
0
对记事本挂上符号表,再 f5 ,能学到的比这个还多。
morning 1 2011-11-23 12:11
16
0
估计是想分析不同编码的文件编译器如何处理的.这个目标本身估计很多人研究过.
gds117 2011-11-23 12:28
17
0
哇。。。。。学习
ImageBase 1 2011-11-23 23:27
18
0
你研究的是什么版本?
CaiHuan 2011-11-23 23:39
19
0
这个是不是说研究感染ml.exe程序....然后让他编译的文件全部都染上后门程序?就像以前那个unix的牛X人做的那样?
游客
登录 | 注册 方可回帖
返回