目的:了解使用ida工具和破解分析(题目如下)
请使用IDA等工具逆向分析Sample_1、Sample_2以及Sample_3这三个exe文件。
说明:
1、IDA Pro下载链接:https://pan.baidu.com/s/1ErHbDG70jD-bg9eW_rEPsA
提取码:91sc
1、IDA查看地址方法:Options->General,在Line prefixes前面打勾。
2、Sample_1分析范围:0x00401000至0x0040104A;Sample_2分析范围:0x00401000至0x0040105E;Sample_3分析范围:0x00401000至0x00401062。
3、写出关键API函数的功能、重点参数的含义以及流程跳转逻辑等
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
实验开始准备阶段:
首先下载资源,然后把自己电脑的杀毒软件关掉,因为Sample_3带有自动复制文件的功能,会被杀毒软件认为是病毒。所以要把杀毒软件关掉。
---------------------------------------------------------------------------------------------------------------------------------------------------------------------
Sample_1
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------
1、Sample_1
通过cmd运行exe文件,首先要添加文件的后缀名,如果没有后缀名,cmd不能识别这个文件。
cmd运行程序如下:
分析代码如下:
.text:00401000 _wWinMain@16 proc near
-- WinMain@16是Windows的图形界面的启动函数,VC里面的启动部分会调用这个开始程序的运行。
-- proc near 如果你的子程序和主程序在同一个代码段,则使用near,调用发生后,主程序堆栈中只压入ip值;如果你的子程序和主程序不在一个代码段,则使用far,调用发生后,主程序堆栈中将压入cs、ip值;
注:
链接:
(3)http://blog.sina.com.cn/s/blog_6471e1bb0100i3mr.html - 关于汇编中proc near与proc far的用法
.text:00401000
.text:00401000 hInstance= dword ptr 8
.text:00401000 hPrevInstance= dword ptr 0Ch
.text:00401000 lpString1= dword ptr 10h
.text:00401000 nShowCmd= dword ptr 14h
解释:所有参数的大小都是dword,也就是4字节,而总共4个参数,那么就是16个字节大小,转换成16进制,就变成了10H,所以在函数调用结束后,需要10H的大小来恢复栈。
HINSTANCE 是“句柄型”数据类型。相当于装入到了内存的资源的ID。HINSTANCE对应的资源是instance.句柄实际上是一个 无符号长整数。但它是“句柄型”,所以你不能把它当成真的无符号长整数,拿来派别的用处,例如,不能拿来做四则运算。HINSTANCE常出现在 API 程序:
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInst, LPSTR lpszCmdLine, int nCmdShow)
注:
.text:00401000
.text:00401000 push ebp ; 保护先前EBP指针, EBP入栈
.text:00401001 mov ebp, esp ; 设置EBP指针指向栈顶
.text:00401003 mov eax, [ebp+lpString1]; 调用参数lpString1(通过栈顶指针EBP地址偏移10h得到lpString1的地址)
.text:00401006 push offset String2 ; "2012"
.text:0040100B push eax ; lpString1
这两句push应该是在压参数。
.text:0040100C call ds:lstrcmpW
.text:00401012 push 0 ; uType
.text:00401014 push offset Caption ; "MESSAGE"
.text:00401019 test eax, eax
.text:0040101B jnz short loc_4010
解释:
随后call 调用lstrcmpW()函数,也就是 eax = lstrcmpW(eax,"2012"),
如果在这里传参“2012” 赋值给 eax,则lstrcmpW()返回 0 给 eax,
之后test eax, eax eax为0,ZF置1,
然后jnz short loc_401035 ,ZF位1不为0,则不跳转,执行以下命令:
注:Test对两个参数(目标,源)执行AND逻辑操作,并根据结果设置标志寄存器,结果本身不会保存。
TEST AX,BX 与 AND AX,BX 命令有相同效果,只是Test指令不改变AX和BX的内容,而AND指令会把结果保存到AX中
举例:
Test用来测试一个位,例如寄存器:
test eax,100b;b后缀意为二进制
jnz ******;如果eax右数第三个位为1,jnz将会跳转
我是这样想的,jnz跳转的条件是ZF=0,ZF=0意味着ZF(零标志)没被置位,即逻辑与结果为1。
2.Test的一个非常普遍的用法是用来测试一方寄存器是否为空:
test ecx, ecx
jz somewhere
如果ecx为零,设置ZF零标志为1,jz跳转。
.text:0040101D push offset Text ; "Hello! 2012" 入栈Hello!2012
.text:00401022 call ds:GetActiveWindow 调用GetActiveWindow函数
解释:
GetActiveWindow:根据直观意思很容易理解 就是获取 活动窗口的句柄
链接:
.text:00401028 push eax ; hWnd 把eax入栈
.text:00401029 call ds:MessageBoxW 调用MessageBoxW函数
注:
MessageBox指的是显示一个模态对话框,其中包含一个系统图标、 一组按钮和一个简短的特定于应用程序消息,如状态或错误的信息。消息框中返回一个整数值,该值指示用户单击了哪个按钮。
链接:
https://baike.baidu.com/item/MessageBox/3846797 -- MessageBoxW
.text:0040102F xor eax, eax XOR运算 按位异或^
.text:00401031 pop ebp 出栈
.text:00401032 retn 10h 返回跳转到10h
注:
.RETN/RETF指令:
按照前面讲CALL指令举的那个例子,CALL指令是进入子程序的指令,而例子中所说的跳出子程序这一过程也需要2条指令,它们是RETN/RETF。
RETN/RETF是跳出子程序的指令,被称为返回指令。RETN指令用于从段内转移CALL进的子程序中返回;RETF指令用于从段间转移CALL进的子程序中返回。
RETN/RETF在反汇编代码中呈现的形式如下:
RETN
RETN 操作数1
RETF
RETF 操作数1
RETN等价于一条指令:POP eip
RETF等价于两条指令:
POP eip
POP CS
而带有操作数的RETN/RETF指令则是在POP之后,执行ESP=ESP+操作数1。
解释:
出现 "Hello! 2012"对话框。
如果不给eax传参,则最终 jnz short loc_401035 ,跳转到地址401035处:
.text:00401035 push offset aHelloWindows ; "Hello! Windows"
.text:0040103A call ds:GetActiveWindow
.text:00401040 push eax ; hWnd
.text:00401041 call ds:MessageBoxW
直接F5进C语言:
注:
链接:
(2)https://blog.csdn.net/ccboby/article/details/6005761 - 汇编里面esp的解释
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------
Sample_2
------------------------------------------------------------------------------------------------------------------------------------------------------------------------
.text:00401000 ; int __stdcall WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
.text:00401000 _WinMain@16 proc near
.text:00401000
.text:00401000 Msg= tagMSG ptr -820h
.text:00401000 String2= byte ptr -804h
.text:00401000 String1= byte ptr -404h
.text:00401000 var_4= dword ptr -4
.text:00401000 hInstance= dword ptr 8
.text:00401000 hPrevInstance= dword ptr 0Ch
.text:00401000 lpCmdLine= dword ptr 10h
.text:00401000 nCmdShow= dword ptr 14h
解释:声明变量,给变量开辟空间
int result; // eax@3
HWND v5; // edi@4
HACCEL v6; // esi@5
struct tagMSG Msg; // [sp+4h] [bp-820h]@5
CHAR String2; // [sp+20h] [bp-804h]@1
CHAR String1; // [sp+420h] [bp-404h]@1
unsigned int v10; // [sp+820h] [bp-4h]@1
int v11; // [sp+824h] [bp+0h]@1
----------------------------------------------------------------------------------------------------------------------------------------------------------------------
.text:00401000
.text:00401000 push ebp
.text:00401001 mov ebp, esp
.text:00401003 sub esp, 820h
.text:00401009 mov eax, ___security_cookie
.text:0040100E xor eax, ebp l XOR运算 按位异或^
ebp ^ __security_cookie压栈保存
.text:00401010 mov [ebp+var_4], eax
v11 ^ __security_cookie;把值保存到ebp_4的地址里
.text:00401013 push esi
.text:00401014 mov esi, [ebp+hInstance]
把寄存器esi入栈,并给esi赋ebp的值,hInstance表示无符号长整数,即把ebp的值转换成无符号长整数(unsigned int类型)再赋给esi
解释:
v10 = (unsigned int)&v11 ^ __security_cookie;
注:
32位CPU有2个32位通用寄存器ESI和EDI。其低16位对应先前CPU中的SI和DI,对低16位数据的存取,不影响高16位的数据。
寄存器ESI、EDI、SI和DI称为变址寄存器(Index Register),它们主要用于存放存储单元在段内的偏移量,用它们可实现多种存储器操作数的寻址方式,为以不同的地址形式访问存储单元提供方便。变址寄存器不可分割成8位寄存器。作为通用寄存器,也可存储算术逻辑运算的操作数和运算结果。 它们可作一般的存储器指针使用。在字符串操作指令的执行过程中,对它们有特定的要求,而且还具有特殊的功能。
SI是源变址寄存器,DI是目的变址寄存器。可以用来存放数据、地址,功能类似、用法类似、一般使用哪个都可以。
但需要注意的是:在串处理指令中,SI用作隐含的源串地址,默认在DS中;DI用做隐含的目的串地址,默认在ES中;此时不能混用。
------------------------------------------------------------------------------------------------------------------------------------------------------------------------
.text:00401017 push 400h ; nSize 入栈0x400u
.text:0040101C lea eax, [ebp+String2] 取得String2的地址
注:lea指令 load effective address, 加载有效地址,可以将有效地址传送到指定的的寄存器。 指令形式是从存储器读数据到寄存器, 效果是将存储器的有效地址写入到目的操作数, 简单说, 就是C语言中的”&”.
.text:00401022 push eax ; lpFilename 入栈String的地址
.text:00401023 push 0 ; hModule 入栈0
.text:00401025 call ds:GetModuleFileNameA 调用GetModuleFileNameA函数
解释:
GetModuleFileNameA(0, &String2, 0x400u);
注:
DWORD GetModuleFileName(
HMODULE hModule,
LPTSTR lpFilename,
DWORD nSize
);
获得hModule所指的文件的名字,
hModule在LoadLibrary之类的函数会返回,是一个句柄,用来标记这个文件资源。
lpFilename是你存放返回的名字的内存块的指针,是一个输出参数,nSize是这个内存块的大小,用于防止溢出。
返回值是用于指示是否发生错误的。
--------------------------------------------------------------------------------------------------------------------------------------
.text:0040102B push 0 ; fCreate
.text:0040102D push 7 ; csidl
.text:0040102F lea ecx, [ebp+String1]
.text:00401035 push ecx ; pszPath
.text:00401036 push 0 ; hwnd
.text:00401038 call ds:SHGetSpecialFolderPathA
类似上面解释,调用函数SHGetSpecialFolderPathA
解释:
SHGetSpecialFolderPathA(0, &String1, 7, 0);
链接:
---------------------------------------------------------------------------------------------------------------------------------------------------------------------
.text:0040103E push offset String2 ; "\\0.exe" 入栈\\0.exe字符串
.text:00401043 lea edx, [ebp+String1] 得到String1的地址
.text:00401049 push edx ; lpString1 入栈String1的地址
.text:0040104A call ds:lstrcatA 调用函数lstrcatA
解释:
lstrcatA(&String1, "\\0.exe");
注:
lstrcat是函数功能:该函数将字符串lpString2附加在另一个字符串lpString1后面。
链接:
------------------------------------------------------------------------------------------------------------------------------------------------------------------------
.text:00401050 lea eax, [ebp+String2] 得到String2的地址
.text:00401056 push eax ; lpString2 把String2的地址入栈
.text:00401057 lea ecx, [ebp+String1] 得到String1的地址
.text:0040105D push ecx ; lpString1 把String1的地址入栈
0040105E call ds:lstrcmpAt 调用lstrcmpAt函数
解释:
lstrcmpA(&String1, &String2)
注:
lstrcmpA函数比较两个参数&String1和&String2地址保存的值
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------
按住F5生成c代码
--------------------------------------------------------------------------------------------------------------------------------------------------------------------
Sample_3
--------------------------------------------------------------------------------------------------------------------------------------------------------------------
Sample_3:
.text:00401000 push ebp 入栈
.text:00401001 mov ebp, esp 把esp给ebp赋值
.text:00401003 mov eax, 2004h 把2004h给eax赋值
.text:00401008 call __alloca_probe 调用_alloca_probe
.text:0040100D mov eax, ___security_cookie 把_security_cookie给eax赋值
.text:00401012 xor eax, ebp XOR运算 按位异或^
ebp ^ __security_cookie压栈保存
.text:00401014 mov [ebp+var_4], eax
v11 ^ __security_cookie;把值保存到ebp_4的地址里
解释:
v3 = (unsigned int)&v4 ^ __security_cookie;
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------
.text:00401017 push 1000h ; nSize 把1000h入栈
.text:0040101C lea eax, [ebp+ExistingFileName] 得到ExistingFileName的地址
.text:00401022 push eax ; lpFilename 把eax入栈
.text:00401023 push 0 ; hModule 把0入栈
.text:00401025 call ds:GetModuleFileNameW 调用GetModuleFileNameW函数
解释:
GetModuleFileNameW(0, &ExistingFileName, 0x1000u);
注:
DWORD GetModuleFileName(
HMODULE hModule,
LPTSTR lpFilename,
DWORD nSize
);
获得hModule所指的文件的名字,
hModule在LoadLibrary之类的函数会返回,是一个句柄,用来标记这个文件资源。
lpFilename是你存放返回的名字的内存块的指针,是一个输出参数,nSize是这个内存块的大小,用于防止溢出。
返回值是用于指示是否发生错误的。
链接:
------------------------------------------------------------------------------------------------------------------------------------------------------------------------
.text:0040102B lea ecx, [ebp+NewFileName] 获得NewFileName的地址
.text:00401031 push ecx ; pszPath 把NewFileName入栈
.text:00401032 push 0 ; dwFlags 把0入栈
.text:00401034 push 0 ; hToken 把0入栈
.text:00401036 push 7 ; csidl 把7入栈
.text:00401038 push 0 ; hwnd 把0入栈
.text:0040103A call ds:SHGetFolderPathW 调用SHGetFolderPathW函数
解释:
SHGetFolderPathW(0, 7, 0, 0, &NewFileName); 获取系统特殊文件夹路径,获取NewFileName的路径
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------
.text:00401040 push offset String2 ; "\\wsample01b.exe" 入栈String2
.text:00401045 lea edx, [ebp+NewFileName] 找到NewFileName的地址
.text:0040104B push edx ; lpString1 把edx入栈
.text:0040104C call ds:lstrcatW 调用lstrcatW 函数
解释:
lstrcatW(&NewFileName, L"\\wsample01b.exe"); 把这两个字符串连接起来
------------------------------------------------------------------------------------------------------------------------------------------------------------------------
.text:00401052 push 0 ; bFailIfExists 把0入栈
.text:00401054 lea eax, [ebp+NewFileName] 找到NewFileName入栈
.text:0040105A push eax ; lpNewFileName 把eax入栈
.text:0040105B lea ecx, [ebp+ExistingFileName] 找到ExistingFileName的地址
.text:00401061 push ecx ; lpExistingFileName 把ecx入栈
.text:00401062 call ds:CopyFileW 调用CopyFileW函数
解释:
CopyFileW(&ExistingFileName, &NewFileName, 0);
注:
copyfileW是C++的一种函数,原型为BOOL CopyFile,返回值为BOOL,非零表示成功,零表示失败,程序例“CopyFileW("C:\\File1.txt","C:\\File2.txt",TRUE)”。
复制文件,与vb的filecopy命令相似
返回值
BOOL,非零表示成功,零表示失败。会设置GetLastError
参数表
参数类型及说明 :
lpExistingFileName String,源文件名
lpNewFileName String,目标文件名
bFailIfExists Long,如果设为TRUE(非零),那么一旦目标文件已经存在,则函数调用会失败。否则目标文件被改写
------------------------------------------------------------------------------------------------------------------------------------------------------------------------
通过本次实验,初步了解了汇编和破解及ida软件的使用,推荐的小甲鱼的汇编系列的课程一部分,小甲鱼的视频教程很好。
通过这次实验的学习,认识了怎样通过ida工具分析exe文件(无源代码)的大致流程,通过对Sample_2样例的分析,我了解了病毒程序怎么在运行的计算机上生成隐藏文件,也可以延伸出我们平时下载一些流氓软件会在我们计算机敏感区生成隐藏文件,我们很难找到,把这些文件删除;通过学习Sample_3样例的分析,我们大致了解到黑客通过什么手段可以从计算机中拷贝走受害者的资料,非法盗取别人文件。通过这些经典样例的学习,我首次进入了网络安全这个神秘世界。我也对网络安全这门课生起了好奇与兴趣