首页
社区
课程
招聘
[原创]手动脱壳学习笔记 OEP->dump->IAT修复
发表于: 2009-1-8 22:56 19095

[原创]手动脱壳学习笔记 OEP->dump->IAT修复

2009-1-8 22:56
19095
大家好,其实是前几天写简历的时候写了自己能手动脱壳,(手动脱壳,从寻找OEP->dump->IAT修复,最后成功拆除)
然而在脱壳方面的文章,一直没时间写,因为本来打算写逆向C++的,但是c++的内容绝不是三几句话能包含的,我打算自己
整理好以后会一并发出来;还忘大家谅解; 由于前不久瑞星电话面试的时候问我是不是能手动脱壳,遇到不熟悉的壳怎么办,
答: OEP-》DUMP—》IAT修复,最后问了找OEP的方法,为了证明下自己能做这个事,所以打算写篇脱壳的文章;当然也是为
了学习。技术方面的东西,我是报着学习的态度面对的;因为我知道我还是只小菜; 嘿嘿。。。废话不说;开始:
1:PE文件的装载
我们知道,一个普通的PE文件存放在磁盘中,在你不点击它之前,它其实和你电脑里的一张图片一样,形象的说来,
它成了个摆设,当鼠标双击它之后,shell调用CreateProcess函数打开一个有效的windows可执行文件,并且创建了
一个内存区对象,为的是稍后将它映射到内存空间中;然后通过调用WIndows的内部函数NtCreateProcess函数,创建了一个
windows执行体对象,以运行该映像;而创建执行体对象涉及以下几步(由创建线程来完成的):

1) 建立EPROCESS块
2)创建初始的进程地址空间
3)初始化内核进程块(KPROCESS)
4)结束地址空间的创建过程
5)建立PEB
6)完成执行体对象的创建过程
由于,本文写的是脱壳,更多详细,请参考 Windows.Internals.Fourth.Edition
PE文件装载过程:(Undocumented Windows)

我们来看一下 loader 是如何解释 PE 文件,又是如何为执行准备内存 image 的。 loader 需要找到空闲的虚拟地址空间来将文件映射到内存。
loader 尝试着将 image 加载在 preferred base address。成功后,loader 将 sections 映射入内存。loader 扫描 section table,
用每一个 section 的 RVA 加上基地址算出 section 的加载地址,然后将 sections 加载在相应的地址上。页属性是根据 section 的特征要求设定的。
将 section 映射入内存后,若基地址不等于 preferred base address,则 loader 开始进行基址重定位。之后检查 import table 并加载所需的 DLLs。
加载 DLL 与加载可执行文件的过程一样——映射 sections,基址重定位,解析
imports等等。所有的 DLL 都加载了之后,就修改 IAT 使之指向实际的 imported 函数的地址。
成了! image 已准备好执行了。关于Load的文章,What Goes On Inside Windows 2000: Solving the Mysteries of the Loader 我放附件里,
由于时间爱你关系,只翻译了一小点,大家凑合看吧;

2 :壳以及壳的加载过程:
1)什么是壳?
   
我们可以把壳看成一个子程序,由它处理后的Pe文件在磁盘中一般是以加密后的形式存在的,有的壳还带有压缩功能,使得exe文件更加小巧,加壳在一定程度上可以防止破解者对程序文件的非法修改,同时可以防止程序被反编译;壳附加在原程序上通过Load载入内存后,却抢先于原程序执行,也就是在PE文件代码段执行之前抢先得到控制权; 然后在执行过程中对原PE文件加密,还原,还原后在把控制权交还给原程序;

2)壳的加载过程
a:保存入口参数,加壳程序初始化时保存各寄存器的值,其实对windows来说,在每个子程序执行之前,总要保存ebx,edi,esi,ebp寄存器的值,而ecx,edx的值是不固定的,不能在返回时应用。特别注意:从 Windows API 函数中返回后,eax,ecx,edx 中的值和调用前不一定相同。当函数返回时,返回值放在eax中。如果您应用程序中的函数提供给 Windows 调用时,也必须尊守这一点,即在函数入口处保存段寄存器和 ebx,esp,esi,edi 的值并在函数返回时恢复。如果不这样一来的话,您的应用程序很快会崩溃。而通常,我们都用pushad/popad  pushfd/popfd指令保存和恢复现场环境,注意:上面说过,壳可以看成一子程序,它只是比没加壳的代码提前获得了控制权,所以基本在没一个壳的开头,总能看到这个指令:
00413000 >  60              pushad   

b:处理多次进入

c:模拟PE加载器完成相应的功能,处理完后将控制权交还给原程序;将控制权交给程序原入口点就是大家熟悉的(OEP)了; 在这一步中,还包括对输入表的处理,重定位表的处理,等; 由于加密加密三书上写的很详细,所以不在重复;
关于壳的加载过程,网上有篇文章:http://blog.chinaunix.net/u1/51827/showart_1757935.html 我转在我的blog上,有兴趣的可以看看;

3:手动脱壳三部曲
1)        查找程序的真正入口(oep)
2)        抓取内存映像文件(dump)
3)        Pe文件重建
到这里,我们可以开始练手了,注意: 不要在不熟悉pe文件格式的情况下,就想着手动脱壳,至少不要在还没搞清楚导入表和导出表,重定位表的情况下就想着更进一步,如果这样,无疑,你是在自找苦吃;
我用的加密解密三的例子:RebPe.exe
好了废话不说,开始三部曲第一部: 寻找OEP
这一部分涉及几种下断的方法和原理,如果不清楚的,赶紧翻开加密解密三,第二章,看吧,最近在看深入浅出MFC,记住一句话:勿在浮沙上筑高台;

寻找OEP的几种方法:
要是你对壳很熟悉,你当然可以一条指令一条指令的来,一直跟踪到代码段,兄弟我除了佩服你的技术精湛之余,还有向你学习的冲动; 但是,我们还是来用用书上的剩下的一些方法看看;

1:内存二次访问断点找OEP

   原理:外壳首先要将原来压缩的代码解压,并放到对应的区块上,处理完毕,将跳到代码段执行。这种方法的关键,要等到代码段解压完毕,再对代码段设置内存访问断点,而一般的壳,会依次对.code .data .rsrc区块进行解压处理,所以,可以现在非代码段上下内存访问断点,此时代码段已经解压,在对代码段设内存访问断点;操作如下:
a:)OD载入RebPe.exe 看到代码如下:
00413000 >  60              pushad
00413001    E8 C2000000     call    004130C8
00413006    2E:3001         xor     byte ptr cs:[ecx], al
00413009    0000            add     byte ptr [eax], al
0041300B    0000            add     byte ptr [eax], al
0041300D    0000            add     byte ptr [eax], al
0041300F    0000            add     byte ptr [eax], al
00413011    003E            add     byte ptr [esi], bh
00413013    3001            xor     byte ptr [ecx], al
00413015    002E            add     byte ptr [esi], ch
b:)Alt+M 打开内存窗口,对rdata设内存访问断点 : F2(,你也可以对其它的非代码段设断;)F9运行,暂停在:
00413145    A4              movs    byte ptr es:[edi], byte ptr [esi>
00413146    B3 02           mov     bl, 2
00413148    E8 6D000000     call    004131BA
0041314D  ^ 73 F6           jnb     short 00413145
在Alt+M ,对.Text设断 F2后,F9
003A0282    61              popad
003A0283    68 30114000     push    401130      ; OEP嘿嘿,看到了吧;
003A0288    C3              retn
003A0289    3011            xor     byte ptr [ecx], dl
我们跟踪,会发现,指令可读性较差,不用担心: ctrl +a ,OD会帮你: 此时,来到如下;也就是我们Pe的真正入口处了:
00401130  /.  55            push    ebp
00401131  |.  8BEC          mov     ebp, esp
00401133  |.  6A FF         push    -1
00401135  |.  68 B8504000   push    004050B8
0040113A  |.  68 FC1D4000   push    00401DFC                         ;  SE 处理程序安装
0040113F  |.  64:A1 0000000>mov     eax, dword ptr fs:[0]
00401145  |.  50            push    eax
00401146  |.  64:8925 00000>mov     dword ptr fs:[0], esp
0040114D  |.  83EC 58       sub     esp, 58
00401150  |.  53            push    ebx
00401151  |.  56            push    esi
00401152  |.  57            push    edi
00401153  |.  8965 E8       mov     dword ptr [ebp-18], esp
00401156  |.  FF15 28504000 call    dword ptr [405028]               ;  kernel32.GetVersion                    ;这个函数熟悉吧
0040115C  |.  33D2          xor     edx, edx
随便用PE查看器,看下入口点:00400000
00401130 – 00400000 = 1130  (入口RVA)
2:堆栈平衡原理找oep
我们说过,在windows中调用子程序前,必须保存现场环境,而当子程序调用之前,必须恢复现场; 这一部分,可能你要,至少要对堆栈有个基本的了解,你可以参考我前面写的逆向C++中,函数,那一节,有一小部分介绍;而要继续深入,推荐arhat的 the shellcode handbook 一书。

我们知道,PUSHAD(Push All 32-bit General Registers)
指令格式:PUSHAD       ;80386+
其功能是把寄存器EAX、ECX、EDX、EBX、ESP、EBP、ESI和EDI等压栈。

POPAD(Pop All 32-bit General Registers)
指令格式:POPAD      ;80386+
其功能是依次把寄存器EDI、ESI、EBP、ESP、EBX、EDX、ECX和EAX等弹出栈,它与PUSHAD对称使用即可。

我们看看堆栈的变化:

Popad未执行前:  

0012FFC4   7C817067  返回到 kernel32.7C817067 ; 当前esp指向
0012FFC8   0012BBC4
0012FFCC   73FB49E4
0012FFD0   7FFD8000
0012FFD4   8054C6B8
执行后:
0012FFA4   0012BBC4    ;edi
0012FFA8   73FB49E4    ;esi
0012FFAC   0012FFF0    ;ebp
0012FFB0   0012FFC4    ;esp
0012FFB4   7FFD8000    ;ebx
0012FFB8   7C92E4F4  ntdll.KiFastSystemCallRet   ;edx
0012FFBC   0012FFB0     ;ecx
0012FFC0   00000000     ;eax
0012FFC4   7C817067  返回到 kernel32.7C817067

对0012FFA4下硬件断点: hr 0012FFA4

F9 ,程序中断在:

003A0282    61              popad
003A0283    68 30114000     push    401130  ;熟悉吧,OEP
003A0288    C3              retn  
其实堆栈平衡原理,是找第一个pushad 配对的popad,因为,在很多壳中,可能在处理数据的时候,还会调用其它子程序,这样,如果用观察法找配对 pushad 和popad
指令就会让初学者,眼花缭乱,相信我,我有过这种经历;所以,用一个硬件断点,方便了很多吧;

脱壳二部曲: dump 抓取内存映像
如果用OD里的dump插件,哈哈,那么IAT你都不用修复拉; OD真的很好用,偶喜欢;好期待有天我也能弄个这NX的插件出来,奉献给大家;不过,有什么用呢; 大牛们都写好了摆好了; 你会发现,脱壳后运行的程序,运行的非常好;
在这里,有一点补充下,有可能你加壳后,会发现文件执行的时候有错误;kanxue补充如下:
将记事本和计算器 中Directory Table 中的LoadConfig值清零,即可加。外壳程序处理时没有考虑LoadConfig。

下面用loadPE来: 右键—》完全转存; dump.Exe 双击,55555.。。。 不能用了吧;
笑不出来了吧;没关系,今天宿舍就停电了,本来打算连IAT修复也写完的,555.。。。我不想在宿舍住拉,每次干得兴起就断电; 那就明天在来吧;今天先到这;

IAT修复:
1:)先来回顾下基础知识:
  首先,PE文件中的数据按照装入内存后的页面属性被分成多个节,并由节表中的数据来描述这些节;一个节中的数据仅仅是属性相同而已,并不一定是同一种用途;
  其次,由于不同用途的数据可能被放在同一个节中,仅仅靠节表是无法确定它的存放的位置的,PE文件中依靠可选头中的数据目录表来指出它们的位置;结构如下:typedef struct _IMAGE_OPTIONAL_HEADER {
     //
     //标准域
     //
     USHORT  Magic;                   //魔数
     UCHAR   MajorLinkerVersion;      //链接器主版本号
     UCHAR   MinorLinkerVersion;      //链接器小版本号
     ULONG   SizeOfCode;              //代码大小
     ULONG   SizeOfInitializedData;   //已初始化数据大小
     ULONG   SizeOfUninitializedData; //未初始化数据大小
     ULONG   AddressOfEntryPoint;     //入口点地址
     ULONG   BaseOfCode;              //代码基址
     ULONG   BaseOfData;              //数据基址
     //
     //NT增加的域
     //
     ULONG   ImageBase;                  //映像文件基址
     ULONG   SectionAlignment;           //节对齐
     ULONG   FileAlignment;              //文件对齐
     USHORT  MajorOperatingSystemVersion;//操作系统主版本号
     USHORT  MinorOperatingSystemVersion;//操作系统小版本号
     USHORT  MajorImageVersion;          //映像文件主版本号
     USHORT  MinorImageVersion;          //映像文件小版本号
     USHORT  MajorSubsystemVersion;      //子系统主版本号
     USHORT  MinorSubsystemVersion;      //子系统小版本号
     ULONG   Reserved1;                  //保留项1
     ULONG   SizeOfImage;                //映像文件大小
     ULONG   SizeOfHeaders;              //所有头的大小
     ULONG   CheckSum;                   //校验和
     USHORT  Subsystem;                  //子系统
     USHORT  DllCharacteristics;         //DLL特性
     ULONG   SizeOfStackReserve;         //保留栈的大小
     ULONG   SizeOfStackCommit;          //指定栈的大小
     ULONG   SizeOfHeapReserve;          //保留堆的大小
     ULONG   SizeOfHeapCommit;           //指定堆的大小
     ULONG   LoaderFlags;                //加载器标志
     ULONG   NumberOfRvaAndSizes;        //RVA的数量和大小
     IMAGE_DATA_DIRECTORY DataDirectory  [IMAGE_NUMBEROF_DIRECTORY_ENTRIES];   //数据目录数组
} IMAGE_OPTIONAL_HEADER, *PIMAGE_OPTIONAL_HEADER;

typedef struct _IMAGE_DATA_DIRECTORY {
     ULONG   VirtualAddress;       //虚拟地址
     ULONG   Size;                 //大小
} IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY

// 各个目录项

// 输出目录
#define IMAGE_DIRECTORY_ENTRY_EXPORT         0
// 输入目录
#define IMAGE_DIRECTORY_ENTRY_IMPORT         1
// 资源目录
#define IMAGE_DIRECTORY_ENTRY_RESOURCE       2
// 异常目录
#define IMAGE_DIRECTORY_ENTRY_EXCEPTION      3
// 安全目录
#define IMAGE_DIRECTORY_ENTRY_SECURITY       4
// 基址重定位表
#define IMAGE_DIRECTORY_ENTRY_BASERELOC      5
// 调试目录
#define IMAGE_DIRECTORY_ENTRY_DEBUG          6
// 描述字符串
#define IMAGE_DIRECTORY_ENTRY_COPYRIGHT      7
// 机器值(MIPS GP)
#define IMAGE_DIRECTORY_ENTRY_GLOBALPTR      8
// TLS(线程本地存储)⑥目录
#define IMAGE_DIRECTORY_ENTRY_TLS            9
// 载入配置目录
#define IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG    10

我们知道,从数据目录表引出的,仅仅是这些数据的RVA和数据块的尺寸,很明显,不同数据块的数据组织方式是不同的,比如导入表和导出表,描述它们的数据结构是不同的;如下:

typedef struct _IMAGE_EXPORT_DIRECTORY {
     ULONG   Characteristics;            //特征
     ULONG   TimeDateStamp;              //时间日期戳
     USHORT  MajorVersion;               //主版本号
     USHORT  MinorVersion;               //小版本号
     ULONG   Name;                       //名字
     ULONG   Base;                       //基址
     ULONG   NumberOfFunctions;          //函数数
     ULONG   NumberOfNames;              //名字数
     PULONG  *AddressOfFunctions;        //函数的地址
     PULONG  *AddressOfNames;            //名字的地址
     PUSHORT *AddressOfNameOrdinals;     //名字序数的地址
} IMAGE_EXPORT_DIRECTORY, *PIMAGE_EXPORT_DIRECTORY;

typedef struct _IMAGE_IMPORT_DESCRIPTOR {
    union {
        DWORD   Characteristics;            // 0 for terminating null import descriptor
        DWORD   OriginalFirstThunk;         // RVA to original unbound IAT (PIMAGE_THUNK_DATA)
    };
    DWORD   TimeDateStamp;                  // 0 if not bound,
                                            // -1 if bound, and real date\time stamp
                                      //  in IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT (new BIND)
                                            // O.W. date/time stamp of DLL bound to (Old BIND)

    DWORD   ForwarderChain;                 // -1 if no forwarders
    DWORD   Name;
    DWORD   FirstThunk;                     // RVA to IAT (if bound this IAT has actual addresses)
} IMAGE_IMPORT_DESCRIPTOR;
typedef IMAGE_IMPORT_DESCRIPTOR UNALIGNED *PIMAGE_IMPORT_DESCRIPTOR;

我们知道,PE文件在没有装入内存之前,导入表结构中由OriginalFirstThunk和FirstThunk指向的

typedef struct _IMAGE_THUNK_DATA32 {
    union {
        PBYTE  ForwarderString;
        PDWORD Function;
        DWORD Ordinal;
        PIMAGE_IMPORT_BY_NAME  AddressOfData;
    } u1;
} IMAGE_THUNK_DATA32;
typedef IMAGE_THUNK_DATA32 * PIMAGE_THUNK_DATA32;

结构数组中的值是相同的,注意,这个结构定义为一个联合,实际上就是一双字,这个结构用来指定一个导入函数,但双字的最高位是1时,表示函数是以序号的方式导入的,这时双字的低位字就是函数的序号,但双字的高位为0时,表示函数以字符类型的函数名方式导入,这时的双字的值就是一个RVA,指向一个结构:

typedef struct _IMAGE_IMPORT_BY_NAME {
    WORD    Hint;
    BYTE    Name[1];
} IMAGE_IMPORT_BY_NAME, *PIMAGE_IMPORT_BY_NAME;
其中Hint表示函数的序号,name【】定义了导入函数的名称;

但是为什么要两个一模一样的IMAGE_THUNK_DATA数组呢,让我们先来回顾下调用导入函数的指令:
在RebPE中找到oep后,F8跟踪,你能看到这个函数:

00401150  |.  53            push    ebx
00401151  |.  56            push    esi
00401152  |.  57            push    edi
00401153  |.  8965 E8       mov     dword ptr [ebp-18], es>
00401156  |.  FF15 28504000 call    dword ptr [405028]     ;  kernel32.GetVersion

Call  dword ptr[405028],  直接寻址方式,将405028处的数据内存属性转换为Dword后作为函数的地址调用;
我们可以在提示窗口中:

ds:[00405028]=7C81126A (kernel32.GetVersion)   

可见,在这个地址中存放的是(kernel32.GetVersion)导入函数的入口地址;这样,call 调用将Eip压入堆栈后,程序执行流就到kernel32中了;

还有一种调用方式:
就是call  aaaaaaaa
Aaaaaaaa   jmp dword ptr 【xxxxxxxx】,这个指令是一个间接寻址的跳转指令,如果你在16进制下查看这个ptr[xxxxxxx]的xxxxxxxx,你会发现它存放的是一个指向欲调用的导入函数名的字符串的RVA地址;当PE文件被装载时,windows装载器根据这个xxxxxxxx处的Rva得到函数名,然后用GetProcAddress函数找到内存中此导入函数的地址,并将xxxxxxxx处的内容替换成真正的函数地址;此时firstthunk指向的DWOWD数组中原本指向函数名的RVA被替换成导入函数的真正入口地址;

在PE文件中,所有DLL对应的导入地址数组在位置上是被排列在一起的,全部这写数组的组合也被称为导入地址表,IAT;导入表中第一个IMAGE_IMPORT_DESCRIPTOR结构的FirstThunk字段指向的就是IAT的起始地址;还有就是数据目录表中的13项,可以直接用来定位IAT的地址; 不过一般还是以上一种方法为准;

到这里,我们可以来思考手动查找IAT的方法了,由于所有DLL模块的firstThunk数组一般都被放在一块,组成了输入地址表,注意: 这里是一般,不是完全,加密解密三上就有IAT被分开存放的例子,大家可以看下;
所以我们只要在找到OEP后,进入到原PE文件的代码块后,找到一个API调用的地址;如:
004011A4  |.  FF15 24504000 call    dword ptr [405024]     ; [GetCommandLineA
004011AA  |.  A3 F8894000   mov     dword ptr [4089F8], ea>

在[405024]右键-》数据窗口中跟随,内存地址;在数据窗口中数据如下:
00405024  AD 2F 81 7C 6A 12 81 7C FA CA 81 7C 1A 1E 80 7C  ?亅j 亅亅  €|
00405034  85 DE 80 7C 6A 3E 86 7C 5F B5 80 7C D7 D6 81 7C  呣€|j>唡_祤|字亅
00405044  77 4B 81 7C 64 A1 80 7C 7B CC 81 7C 98 2F 81 7C  wK亅d|{虂|?亅
00405054  27 CD 80 7C C9 2F 81 7C 82 4B 81 7C 6E 2B 81 7C  '蛝|?亅侹亅n+亅
00405064  88 0F 81 7C 46 2C 81 7C                          ?亅F,亅t泙|

我们再在数据窗口中右键-》长型,地址,可以看到如下:
00405000  7C810EE1  kernel32.GetFileType
00405004  7C80A520  kernel32.GetStringTypeW
00405008  7C838A24  kernel32.GetStringTypeA
0040500C  7C80CD38  kernel32.LCMapStringW
00405010  7C838E00  kernel32.LCMapStringA
00405014  7C809C88  kernel32.MultiByteToWideChar
00405018  7C801D7B  kernel32.LoadLibraryA
0040501C  7C80B731  kernel32.GetModuleHandleA
00405020  7C801EF2  kernel32.GetStartupInfoA
00405024  7C812FAD  kernel32.GetCommandLineA
00405028  7C81126A  kernel32.GetVersion
0040502C  7C81CAFA  kernel32.ExitProcess
00405030  7C801E1A  kernel32.TerminateProcess
00405034  7C80DE85  kernel32.GetCurrentProcess
00405038  7C863E6A  kernel32.UnhandledExceptionFilter
0040503C  7C80B55F  kernel32.GetModuleFileNameA
00405040  7C81D6D7  kernel32.FreeEnvironmentStringsA
00405044  7C814B77  kernel32.FreeEnvironmentStringsW
00405048  7C80A164  kernel32.WideCharToMultiByte
0040504C  7C81CC7B  kernel32.GetEnvironmentStringsA
00405050  7C812F98  kernel32.GetEnvironmentStringsW
00405054  7C80CD27  kernel32.SetHandleCount
00405058  7C812FC9  kernel32.GetStdHandle
0040505C  7C814B82  kernel32.GetEnvironmentVariableA
00405060  7C812B6E  kernel32.GetVersionExA
00405064  7C810F88  kernel32.HeapDestroy
00405068  7C812C46  kernel32.HeapCreate
0040506C  7C809B74  kernel32.VirtualFree
00405070  7C92FF0D  ntdll.RtlFreeHeap
00405074  7C94ABA5  ntdll.RtlUnwind
00405078  7C810E17  kernel32.WriteFile
0040507C  7C812F06  kernel32.GetCPInfo
00405080  7C8099A5  kernel32.GetACP
00405084  7C812837  kernel32.GetOEMCP
00405088  7C9300A4  ntdll.RtlAllocateHeap
0040508C  7C809AE1  kernel32.VirtualAlloc
00405090  7C939B80  ntdll.RtlReAllocateHeap
00405094  7C80AE30  kernel32.GetProcAddress
00405098  00000000
0040509C  77D2F3C2  USER32.SendMessageA
004050A0  77D2E8F6  USER32.LoadIconA
004050A4  77D2B19C  USER32.DestroyWindow
004050A8  77D2AAFD  USER32.PostMessageA
004050AC  77D24A4E  USER32.EndDialog
004050B0  77D3B144  USER32.DialogBoxParamA
004050B4  00000000

上下拉拉看,找出IAT的起始地址,和结束地址: 这里是:00405000 和004050B8 大小: B8

下面我们就用Import REC来修复:
1)载入RebPE,定位到OEP处,00401130处,打开ImportREC这个软件,从进程下拉列表中找到RebPE后,在OEP中填入正确的OEP这里是; 1130

2)我们在上面已经找出了IAT的起始地址和大小,我们在这里填入: 5000 和B8;


3)单击Get Import按钮,分析IAT得到的基本信息,如果都能正确识别,选择Add new section ,单击FixDump按钮,选择我们抓取的映像,(注意,此例抓取内存映像的时候要修正ImageSize),修复完成后,得到:dump_.exe文件; 运行,

[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)

上传的附件:
收藏
免费 0
支持
分享
最新回复 (6)
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
谢谢分享!!!!!
2009-1-15 03:51
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
thanks
2009-1-15 17:29
0
雪    币: 36
活跃值: (10)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
4
其实修复挺难,要好好学习下。
2011-3-22 15:08
0
雪    币: 93
活跃值: (57)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
5
学习并下载!
2012-3-12 17:25
0
雪    币: 57
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
6
嗯  不错 写的很祥细 对初学者很有帮助
2012-3-19 10:29
0
雪    币: 334
活跃值: (92)
能力值: ( LV7,RANK:100 )
在线值:
发帖
回帖
粉丝
7
作个记号,以后慢慢深入
2012-4-9 14:30
0
游客
登录 | 注册 方可回帖
返回
//