网络安全学习第1篇 - 简单使用ida工具和破解分析

实验目的

目的:了解使用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运行程序如下:

网络安全学习第1篇 - 简单使用ida工具和破解分析_第1张图片

网络安全学习第1篇 - 简单使用ida工具和破解分析_第2张图片

网络安全学习第1篇 - 简单使用ida工具和破解分析_第3张图片

 

网络安全学习第1篇 - 简单使用ida工具和破解分析_第4张图片

 

分析代码如下:

 

.text:00401000 _wWinMain@16 proc near

 -- WinMain@16是Windows的图形界面的启动函数,VC里面的启动部分会调用这个开始程序的运行。

 -- proc near 如果你的子程序和主程序在同一个代码段,则使用near,调用发生后,主程序堆栈中只压入ip值;如果你的子程序和主程序不在一个代码段,则使用far,调用发生后,主程序堆栈中将压入cs、ip值;

 

注:

  1. 对于一个不是GUI(图形界面)的程序而言,开始的函数叫做main,对于一个非图形界面的程序来说可能没有定义这个WinMain@16函数,所以就无法找到WinMain@16。找不到就无法启动这个程序。所以你应该查看是否存在函数WinMain,如果不存在,看看是否存在main,如果存在main,修改为控制台(CONSOLE)类型就可以了,有些系统的入口点是_tmain等等,具体看手册。

 

  1. CS为代码段寄存器,IP为指令指针寄存器,从名称上我们可以看出它们和指令的关系。在CPU中,程序员能够用指令读写的部件只有寄存器,程序员可以通过改变寄存器中的内容实现对CPU的控制。CPU从何处执行指令是由CS、IP中的内容决定的,程序员可以通过改变CS、IP中的内容来控制CPU执行目标指令。

 

链接:

(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)

 

注:

  1. 汇编语言中PTR的含义(很全) 汇编里面ptr 是规定的字(既保留字),是用来临时指定类型的。 mov ax,word ptr [bx]; 是把内存地址等于“BX寄存器的值”的地方所存放的数据,赋予ax。 ... 总结,既有寄存器时可以,且一般不用ptr;没有时一定要用(防止当两个操作数的宽度不一样)。

 

 

.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:根据直观意思很容易理解 就是获取  活动窗口的句柄

链接:

https://blog.51cto.com/qianqianquege/1427059 --GetActiveWindow 这个函数是干嘛的

 

 

.text:00401028  push    eax          ; hWnd  把eax入栈

.text:00401029  call    ds:MessageBoxW 调用MessageBoxW函数

 

注:

MessageBox

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语言:


网络安全学习第1篇 - 简单使用ida工具和破解分析_第5张图片

网络安全学习第1篇 - 简单使用ida工具和破解分析_第6张图片

注:

  1. eax, ebx, ecx, edx, esi, edi, ebp, esp等都是X86 汇编语言中CPU上的通用寄存器的名称,是32位的寄存器。 如果用C语言来解释,可以把这些寄存器当作变量看待。 ... 这些32位寄存器有多种用途,但每一个都有“专长”,有各自的特别之处。
  2. ESP是一直指向栈顶的指针
  3. 而EBP只是存取某时刻栈顶指针,以方便对栈的操作,如获取函数参数、局部变量等

 

链接:

(1)https://blog.csdn.net/zsjum/article/details/6117043  - 通过一段汇编,加深对寄存器ESP和EBP的理解。

(2)https://blog.csdn.net/ccboby/article/details/6005761 - 汇编里面esp的解释

 

 

-----------------------------------------------------------------------------------------------------------------------------------------------------------------------

                                                                                        Sample_2

------------------------------------------------------------------------------------------------------------------------------------------------------------------------

 

 

  1. sample_2.exe

.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;

 

注:

32CPU232位通用寄存器ESIEDI。其低16位对应先前CPU中的SIDI,对低16位数据的存取,不影响高16位的数据。

寄存器ESIEDISIDI称为变址寄存器(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);

链接:

https://blog.csdn.net/itcastcpp/article/details/3556210 -- Windows API SHGetSpecialFolderPath的用法 ,获取特殊文件夹

 

 

---------------------------------------------------------------------------------------------------------------------------------------------------------------------

 

 

.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后面。

链接:

https://baike.baidu.com/item/lstrcat -- lstrcat

 

------------------------------------------------------------------------------------------------------------------------------------------------------------------------

.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代码

 

 

 

 

 

网络安全学习第1篇 - 简单使用ida工具和破解分析_第7张图片

网络安全学习第1篇 - 简单使用ida工具和破解分析_第8张图片

网络安全学习第1篇 - 简单使用ida工具和破解分析_第9张图片

 

--------------------------------------------------------------------------------------------------------------------------------------------------------------------

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是这个内存块的大小,用于防止溢出。
返回值是用于指示是否发生错误的。

 

链接:

https://zhidao.baidu.com/question/13996574.html -- GetModuleFileName函数的作用?及各参数的意思?

 

------------------------------------------------------------------------------------------------------------------------------------------------------------------------

 

.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(非零),那么一旦目标文件已经存在,则函数调用会失败。否则目标文件被改写

 

------------------------------------------------------------------------------------------------------------------------------------------------------------------------

网络安全学习第1篇 - 简单使用ida工具和破解分析_第10张图片

 

网络安全学习第1篇 - 简单使用ida工具和破解分析_第11张图片

 

本次实验的体会

通过本次实验,初步了解了汇编和破解及ida软件的使用,推荐的小甲鱼的汇编系列的课程一部分,小甲鱼的视频教程很好。

通过这次实验的学习,认识了怎样通过ida工具分析exe文件(无源代码)的大致流程,通过对Sample_2样例的分析,我了解了病毒程序怎么在运行的计算机上生成隐藏文件,也可以延伸出我们平时下载一些流氓软件会在我们计算机敏感区生成隐藏文件,我们很难找到,把这些文件删除;通过学习Sample_3样例的分析,我们大致了解到黑客通过什么手段可以从计算机中拷贝走受害者的资料,非法盗取别人文件。通过这些经典样例的学习,我首次进入了网络安全这个神秘世界。我也对网络安全这门课生起了好奇与兴趣

 

 

 

 

 

 

 

 

 

你可能感兴趣的:(网络安全)