恶意代码分析实战 Lab 9-2 习题笔记
Lab 9-2
问题
1.在二进制文件中,你看到的静态字符串是什么?
解答: 我们先查看一下静态字符串的在IDA
中,这里用IDA
来查看比较方便点
这里显示了在二进制文件中的静态字符串
2.当你运行这个二进制文件时,会发生什么?
解答: 运行我就不运行了,书上说了运行之后立刻就退出了,所以还要重置虚拟机,懒得做了,运行会发生的就是立刻退出了这个函数
下面开始二进制分析
这里我用的是吾爱**版的OD,这个比较好使(可以很好的显示中文),唯一缺点就是无法调字体
函数从开始运行,到这里会调用第一个不是系统函数的函数,叫00401F08
,我们可以进去看看是什么
这个函数没有什么分析的价值,是函数的堆初始化函数
然后下一个不是系统调用的函数是这个
一个叫00401D5D
的函数
然后进来之后的第一个调用是这个
一个叫004023C0
的函数,我们进去看看
这里我们注意到这个函数有个GetStartupInfoA
,这个MSDN
中是这样说的
检索调用进程创建时指定的STARTUPINFO结构的内容
这个也是初始化函数,还没有到main
函数
然后下一个函数
紧接的就是下一个函数
这里调用了GetCommandLineA
这个函数
这个函数是获得用户输入的命令参数的
我们可以看到这个函数调用完之后
EAX
的值马上就变成了我们当前目录的,也就是argv[0]
的值
然后下一个函数是
这个叫004019DE
的函数
函数进来的第一个调用是GetEnvironmentStringsW
在MSDN
中,我们可以看到返回值
Return value
If the function succeeds, the return value is a pointer to the environment block of the current process.If the function fails, the return value is NULL.
然后我们能运行OD
查看这个函数的执行结果
这个EAX
的值是个指针,我们去查看这个地址上的值
这里就是我们运行这个文件所在的PATH
然后下面就是下一个调用,这次调用的是GetEnvironmentStrings
,这个和上面那个函数差不多,我们看看返回值,不过这里被jmp
跳转了,这个函数不会被执行
然后就会一直在这里做循环,这个循环是一次比较每个字符的操作
然后这里一直运行下去就会结束了
然后我们按照书上的做法,在main
函数的开始(0x401128
)放一个断点,然后运行
然后这样我们就进来了main
函数
函数的第一个调用是GetModuleFileNamed
这个,我们看看返回值
注意看入参
这时候的入参PathBuffer
已经变成12FC80
了,我们跳到那个内存地址上
这里还是0
,然后我们运行调用
这个地址马上变了,然后我们查看值,就是我们这个文件所在的目录
然后下一个调用
这里我们在IDA
中找找对应,防止走丢
这个函数在IDA
中已经标识出来,是_strrchr
,这个函数是个C++
的函数,用来查找字符的
我们注意到上面push
了两个参数入栈
最后一个push
入栈的是ecx
,然后看看值
ECX
在这里的值是这个可执行文件的绝对路径,也就是要源字符串,然后我们的要查找的字符是5Ch
这个东西也就是\
这个字符
然后这个函数的返回值就是指向最后一个\
字符的位置,我们运行看看
和我们预想的一样,这个函数返回了最后一个\
字符的位置
然后继续下一个调用
在IDA
中对应的是
这是一个字符串比较的函数_strcmp
这里两个入参是ecx
和eax
,我们看看这两个值是多少
这个字符串就是书中那个被混淆的字符串
然后另一个字符串的话,我们回到上面创建字符串那里,然后运行到这些赋值完成
我们知道0x0
在二进制的世界就是代表字符串结束的那个\0
字符,所以我们在ebp
上查找这两个字符
此时的ebp
是这里写代码片
然后我们右键在内存中找到这个地址
第一个字符串的地址是
也就是12FF80
-1B0
=12FDD0
这里你也可以看见下一个字符串是ocl.exe
如果我们用这种方法去找下一个0x0
隔开的第二个字符串的话
这个值就是ebp-0x1A0
也就是
12FF80
-1A0
=12FDE0
也就是刚刚那个下面的位置
然后继续分析刚刚函数
这里将两个字符进行比较
程序预期的程序名字应该是ocl.exe
这个字符
然后调用完之后用test
判断一下返回值
如果返回值是0
的话,就一个je
跳转,否则就结束函数
然后下面mov edx, 0x1
,将edx
赋值为1
然后测试一下,这里注定是不会跳转的
然后继续调用WSAStartup
函数,这是调用winsock dll
之前的初始化步骤
我们看看12FDE8
存了什么
什么也没有
调用完函数之后就变成这样了
然后函数下一步准备进行这个操作
初始化了一个TCP
的socket
这些操作都做完之后,就会准备调用这个函数了
我们在IDA
里面看看
这个函数在IDA
里面并没有很好的显示出来,估计不是一个常用的C函数
然后我们进入这个函数分析看看
函数之前的一个入参是这个
此时eax
的值是
我们也是进入这个函数看看
这个函数最后会返回一个0Ch
如果你用的是这个字符串的话
在函数将要返回的最后,这个字符串通过各种变换之后,将网站变了出来
所以这个函数的作用应该就是个网址的
然后下面的函数准备开始连接这个网址
这里函数连接失败,因为这个网址不存在,所以这里会结束连接,并做一些清理工作
然后注意这里会sleep
一个30000ms
的时间
我们这里重新执行这个代码,然后在判断那里不要跳转关闭连接
这里我们就用上我们的DNS fake
和Inetsim
,这里将机器的DNS
设置我们DNS fake
的ip,然后DNS fake
就会将这个代码请求的域名返回成我们的inetsim
的地址,然后下面就是这个恶意代码去连接这个inetsim
这里我们设置DNS fake
了之后,这里的gethostbyname()
就返回正确了
红线显示我们就会跳过closesocket
这个函数
然后我们下一个函数
这个函数是ntohs
,这是个网络初始化函数
ntohs函数将u_short从TCP / IP网络字节顺序转换为主机字节顺序(这在英特尔处理器上是小端的)
然后下一个函数不出所料就是connect
了
然后我们连接成功之后,就会遇到下一个函数
然后我们进去看看
然后我们进入发现了一个函数的调用,但是OD
并没有标注这个函数,我们第一感觉是要看看这个是不是系统函数调用,然后我们用IDA
来打开看看
这个函数在IDA
里面标注是_memset
这个函数
这是函数的初始化函数,不用管
然后又遇到个函数,我们也是看IDA
在IDA
的0040102C
这个地方(也就是上面那个函数的下面),是这个函数
这个也是初始化函数,我们不理他
然后下面一个函数就开始创建一个进程了
从OD
的LastError
我们可以看到,这个创建进程的函数创建进程成功了
然后下一个函数就是这个
WaitForSingleObject 函数用来检测 hHandle 事件的信号状态,当函数的执行时间超过 dwMilliseconds 就返回,但如果参数 dwMilliseconds 为 INFINITE 时函数将直到相应时间事件变成有信号状态才返回,否则就一直等待下去,直到 WaitForSingleObject 有返回直才执行后面的代码
所以这里会一直等待相应的时间,这个事件是hObject
=7C81D63B
这个东西
然后这个函数做完这些就会返回了
然后这个函数就可以命名为这个
CreateProcessAndWaitSignal
,当然,这是我的个人命名方式,这个函数会创建一个进程来处理和服务器的连接,然后主进程继续执行
这里有个技巧,就是直接将sleep
用nop
填充了,省的它执行浪费时间
然后这里函数又会跳回到原来初始化socket
那个地方
这里我们刚刚分析少了一个地方就是ntohs
这个函数的的入参,这里的入参是
从这里可以看出来,这个是个定值,0x270Fh
=9999d
所以这里它会去连接恶意域名的9999
端口
然后我们这里是第二遍执行,这里connect
的返回值是-1d
,因为我们的inetsim
并没有开9999
端口
然后这里就这样一直循环连接,书中的提示是在这里会有一个反向shell
的创建
也就是_memset
这个地方
这里这个地方的,这里已隐藏模式创建了一个cmd.exe
的窗口
这里是哪里指示这是个隐藏的窗口,这里隐藏窗口并不是在这里创建
是这里,这里设置了wShowWindow
这个选项为SW_HIDE
然后在这里
设置了hStdInput
和hStdError
和hStdOutput
三个值都为一个套接字,也就是我们刚刚连接那个套接字
所以到这里,这个代码基本分析完毕了,这个代码没前一个那么复杂和庞大
3.怎样让这个恶意代码的攻击载荷(payload)获得运行
解答: 将这个代码的名字改为ocl.exe
4.在地址0x00401133处发生了什么?
解答: 在地址那里是将一个一个字符赋值的过程
这里的0
代表了字符串结束符\0
5.传递给子列程(函数)0x401089的参数是什么?
解答: 这个函数的参数在OD
中可以很清晰的看出来
就是那个加密的字符串
6.恶意代码使用的域名是什么?
解答: 这个恶意域名其实就是上一个问题的返回值
也就是那个作者的域名哈哈哈
7.恶意代码使用什么编码函数来混淆域名?
解答: 这个可以试着分析分析看,书上说是异或加密
的,我们试着分析看看
函数在开始的时候,第一个调用的函数是_strlen
这个
这个_strlen
会计算输入的字符的长度
然后函数的返回值是0xCh
也就是12d
然后函数就会做下面这些操作
这里的esp
是存储着加密字符串的地址
这个地址我们找一下
add esp, 0x4
这个操作之前,esp
的值是0012FB54
这个,加了0x4
之后就是0012FB58
然后就会指向stack
里面的下一个值
这个地址上的栈存储着0012FD8E
这个值
然后下面的local.65
的值是ebp-0x104
,ebp
是0012FC64
,最后结果的地址就是0012FB60
然后这个地址的值就变成了C
然后下一个变量是local.66
的地址是是ebp-0x108
,也就是0012FB5C
这里给这个变量赋值为0
然后就是一个无条件跳转JMP
,然后跳转到了下面这里
这里我们刚刚上面才赋值local.66
为0
,然后调用cmp
指令,注意这个0x20
是恶意代码作者写进去的,不是根据strlen
计算得到的
然后这个跳转有点变态,跳转条件是SF=OF
这里的SF
是符合标志位,如果结果为负数则其值为1
,如果为正数,则为0
然后OF
是溢出标志位,溢出的话,值则为1
,否则为0
,cmp
函数的其实是减运算,只是值不保存
我们这里的local.66
=0
,减去0x20
之后,值为负数,SF
=1
,值没有溢出,OF
=0
,所以SF
<>OF
,这里不会跳转
然后将入参arg.2
的值赋值给了edx
,我们看看这个入参是多少
然后这里我们看看内存和栈中的值
栈中的指示像是个指针,但是这个指针地址比较大,感觉不太可能有这么大的指针,在内存中一跳,就会发现这个内存地址不存在
下一步是一个add
操作
这里将local.66
的值和edx
相加,edx
值不变
下一步就是一个movsx
指令,将值带符号扩展之后赋值给ecx
原来的值部署54160646h
嘛,movsx
只看后八位也就是46
=01000110
,第一个字符是0
,所以这个扩展之后就是00000046h
了
然后一个mov
将eax
的值赋值成为0
现在ecx
的值已经变成46h
了
下一个指令是cdq
,这是将eax
扩展成为edx:eax
的方法,没做这个操作之前的eax
是0x0h
,edx
是0012FD90
,做了这个操作之后,连同edx
都被赋值成了0
idiv
当除数是 32 位的时候,(现在就是),然后local.65
还是等于0xCh
然后这里的除数是local.65
,被除数是edx:eax
,也就是edx:eax
/local.65
这个意思
结果的商存储在eax
里面,然后余数存储在edx
里面,注意这里是带符号的除法
OD
里面做完这个指令之后,EAX
和EDX
并没有变化
这里将加密字符串赋值给eax
,然后带符号扩展edx
最终的edx
变成了0x31h
然后是这样的
然后将这个值传递给这里之后就跳转了
这里分析时候实在看不懂,算了,不分析算法了
8.恶意代码在0x0040106E处调用CreateProcessA函数的意义是什么?
书上的解答是
恶意代码设置stdout、stderr和stdin的句柄到socket(被用在CreateProcessA的STARTUPINFO结构中)。由于cmd作为CreateProcessA的参数调用,因此通过绑定一个套接字与命令shell来创建逆向shell
大概意思就是说这个shell
是通过这个CreateProcessA
的STARTUPINFO
来绑定stdou
,stderr
,stdin
在cmd
上的,只有这样才能把这些东西绑定在cmd
上
本文完