PE 快速入门
本帖最后由 whypro 于 2010-5-23 13:28 编辑由于上一篇文章太长没法缩进(懒得用鼠标!),所以另起一贴来说yC 逆着看。
其中的 call CryptFile 函数尤为重要!(因为yoda's Crypter加壳软件核心算法都在函数CryptFile中)
对应反汇编如下:
00402B1E|> \57 push edi
00402B1F|.FF75 08 push dword ptr ss:
00402B22|.68 54104000 push yC.00401054
00402B27|.E8 D4EBFFFF call yC.00401700
进入 call yC.00401700 后如下:
00401700/$55 push ebp
00401701|.8BEC mov ebp,esp
00401703|.E8 CB100000 call yC.004027D3 ;随机种子
00401708|.6A 00 push 0 ; /hTemplateFile = NULL
0040170A|.68 80000000 push 80 ; |Attributes = NORMAL
0040170F|.6A 03 push 3 ; |Mode = OPEN_EXISTING
00401711|.6A 00 push 0 ; |pSecurity = NULL
00401713|.6A 03 push 3 ; |ShareMode = FILE_SHARE_READ|FILE_SHARE_WRITE
00401715|.68 000000C0 push C0000000 ; |Access = GENERIC_READ|GENERIC_WRITE
0040171A|.FF75 08 push dword ptr ss: ; |FileName
0040171D|.E8 2A140000 call <jmp.&KERNEL32.CreateFileA> ; \CreateFileA
00401722|.83F8 FF cmp eax,-1
00401725|.0F84 79030000 je yC.00401AA4
0040172B|.A3 84114000 mov dword ptr ds:,eax
00401730|.6A 00 push 0 ; /pFileSizeHigh = NULL
00401732|.FF35 84114000 push dword ptr ds: ; |hFile = NULL
00401738|.E8 1B140000 call <jmp.&KERNEL32.GetFileSize> ; \GetFileSize
0040173D|.0BC0 or eax,eax
0040173F|.75 10 jnz short yC.00401751
00401741|.FF35 84114000 push dword ptr ds: ; /hObject = NULL
00401747|.E8 FA130000 call <jmp.&KERNEL32.CloseHandle> ; \CloseHandle
0040174C|.E9 61030000 jmp yC.00401AB2
00401751|>A3 68114000 mov dword ptr ds:,eax
00401756|.A1 68114000 mov eax,dword ptr ds:
0040175B|.83C0 60 add eax,60
0040175E|.05 E6090000 add eax,9E6
00401763|.0305 EC154000 add eax,dword ptr ds:
00401769|.A3 6C114000 mov dword ptr ds:,eax
0040176E|.50 push eax ; /MemSize
0040176F|.6A 40 push 40 ; |Flags = GPTR
00401771|.E8 F4130000 call <jmp.&KERNEL32.GlobalAlloc> ; \GlobalAlloc
00401776|.0BC0 or eax,eax
00401778|.75 10 jnz short yC.0040178A
0040177A|.FF35 84114000 push dword ptr ds: ; /hObject = NULL
00401780|.E8 C1130000 call <jmp.&KERNEL32.CloseHandle> ; \CloseHandle
00401785|.E9 0C030000 jmp yC.00401A96
0040178A|>A3 64114000 mov dword ptr ds:,eax
0040178F|.6A 00 push 0 ; /pOverlapped = NULL
00401791|.68 5C114000 push yC.0040115C ; |pBytesRead = yC.0040115C
00401796|.FF35 68114000 push dword ptr ds: ; |BytesToRead = 0
0040179C|.FF35 64114000 push dword ptr ds: ; |Buffer = NULL
004017A2|.FF35 84114000 push dword ptr ds: ; |hFile = NULL
004017A8|.E8 C9130000 call <jmp.&KERNEL32.ReadFile> ; \ReadFile
对应源码为
CALL InitRandom
;----- MAP THE FILE -----
invoke CreateFile,szFname,GENERIC_WRITE + GENERIC_READ,FILE_SHARE_WRITE + FILE_SHARE_READ,\
NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,0
cmp eax,INVALID_HANDLE_VALUE
jz FileErr
mov hFile,eax
invoke GetFileSize,hFile,0
.IF eax == 0
push hFile
call CloseHandle
jmp FsizeErr
.ENDIF
mov dwFsize,eax
mov eax,dwFsize
add eax,IT_SIZE
add eax,DEPACKER_CODE_SIZE
add eax,ALIGN_CORRECTION
mov dwOutPutSize,eax
push eax
push GMEM_FIXED + GMEM_ZEROINIT
call GlobalAlloc
.IF eax == NULL
push hFile
call CloseHandle
jmp MemErr
.ENDIF
mov pMem,eax
invoke ReadFile,hFile,pMem,dwFsize,offset dwBytesRead,NULL
下面讲解一下关键函数!
1.call yC.004027D3 函数调用了
GetTickCount 用来返回机器启动后的毫秒数,然后用变量存了起来!相当于c语言中的
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main ()
{
printf ("First number: %d\n", rand() % 100);
srand ( time(NULL) );
printf ("Random number: %d\n", rand() % 100);
srand ( 1 );
printf ("Again the first number: %d\n", rand() %100);
return 0;
}
2.CreateFile->GetFileSize->GlobalAlloc->ReadFile
总体来说这几个api就是把文件导入到内存中。 本帖最后由 源少 于 2010-5-22 21:43 编辑
我晕,手机看不到验证码,无法回复,只能编辑先前的回复了,汗。谢谢!我还是先学基础,这些未能理解。 进来认真学习,楼主高产。。。距离越来越大 lgjxj 曾经纵横于天地间,其威震三界。跨界大战,追杀淂太古君王都惶惶如丧家之犬 !!!!! 弄一个电子文档看着比较舒服一些。文章好长呀 本帖最后由 whypro 于 2010-5-22 21:45 编辑
不会吧,蚊帐才开始!
00401708|.6A 00 push 0 ; /hTemplateFile = NULL
0040170A|.68 80000000 push 80 ; |Attributes = NORMAL
0040170F|.6A 03 push 3 ; |Mode = OPEN_EXISTING
00401711|.6A 00 push 0 ; |pSecurity = NULL
00401713|.6A 03 push 3 ; |ShareMode = FILE_SHARE_READ|FILE_SHARE_WRITE
00401715|.68 000000C0 push C0000000 ; |Access = GENERIC_READ|GENERIC_WRITE
0040171A|.FF75 08 push dword ptr ss: ; |FileName
0040171D|.E8 2A140000 call <jmp.&KERNEL32.CreateFileA> ; \CreateFileA
CreateFile 函数创建或打开下列对象,并返回一个可以用来访问这些对象的句柄。
文件 pipes 邮槽 通信资源 磁盘驱动器(仅适用于 windowsNT ) 控制台 文件夹(仅用于打开)
HANDLE CreateFile(
LPCTSTR lpFileName, // 指向文件名的指针
DWORD dwDesiredAccess, // 访问模式(写 / 读)
DWORD dwShareMode, // 共享模式
LPSECURITY_ATTRIBUTES lpSecurityAttributes, // 指向安全属性的指针
DWORD dwCreationDisposition, // 如何创建
DWORD dwFlagsAndAttributes, // 文件属性
HANDLE hTemplateFile // 用于复制文件句柄
);
0040172B|.A3 84114000 mov dword ptr ds:,eax
00401730|.6A 00 push 0 ; /pFileSizeHigh = NULL
00401732|.FF35 84114000 push dword ptr ds: ; |hFile = NULL
00401738|.E8 1B140000 call <jmp.&KERNEL32.GetFileSize> ; \GetFileSize
GetFileSize 就两个参数
弟一个是文件句柄
第二个参数比较烦:
应用程序要求文件大小的高字节的返回值。一般不用这个,直接用NULL代替即可。
按照msdn的解释,GetFileSize还是能够取得大于2G的文件大小,如果文件大小大于2G, 大于2G的部分就放在*lpFileSizeHigh里面。
00401751|> \A3 68114000 mov dword ptr ds:,eax
00401756|.A1 68114000 mov eax,dword ptr ds:
0040175B|.83C0 60 add eax,60
0040175E|.05 E6090000 add eax,9E6
00401763|.0305 EC154000 add eax,dword ptr ds:
00401769|.A3 6C114000 mov dword ptr ds:,eax
0040176E|.50 push eax ; /MemSize
0040176F|.6A 40 push 40 ; |Flags = GPTR
00401771|.E8 F4130000 call <jmp.&KERNEL32.GlobalAlloc> ; \GlobalAlloc
例子:
文件映射例子 本帖最后由 whypro 于 2010-5-22 23:42 编辑
接着GlobalAlloc来
该函数用于从全局堆中分配出内存供程序使用,函数原型为:
HGLOBAL GlobalAlloc(
UINT uFlags,
SIZE_T dwBytes
);
uFlags参数含义
GHND GMEM_MOVEABLE和GMEM_ZEROINIT的组合
GMEM_FIXED 分配固定内存,返回值是一个指针
GMEM_MOVEABLE 分配活动内存,在Win32中,内存块不能在物理内存中移动,但能在默认的堆中移动。返回值是内存对象的句柄,用函数GlobalLock可将句柄转化为指针
GMEM_ZEROINIT 将内存内容初始化为零
GPTR GMEM_FIXED和GMEM_ZEROINIT的组合
一般情况下我们在编程的时候,给应用程序分配的内存都是可以移动的或者是可以丢弃的,这样能使有限的内存资源充分利用,所以,在某一个时候我们分配的那块 内存的地址是不确定的,因为他是可以移动的,所以得先锁定那块内存块,这儿应用程序需要调用API函数GlobalLock函数来锁定句柄。如下: lpMem=GlobalLock(hMem); 这样应用程序才能存取这块内存。所以我们在使用GlobalAllock时,通常搭配使用GlobalLock,当然在不使用内存时,一定记得使用 GlobalUnlock,否则被锁定的内存块一直不能被其他变量使用。
GlobalAlloc对应的释放空间的函数为GlobalFree。
0040178F|.6A 00 push 0 ; /pOverlapped = NULL
00401791|.68 5C114000 push yC.0040115C ; |pBytesRead = yC.0040115C
00401796|.FF35 68114000 push dword ptr ds: ; |BytesToRead = D3046 (864326.)
0040179C|.FF35 64114000 push dword ptr ds: ; |Buffer = 01050020
004017A2|.FF35 84114000 push dword ptr ds: ; |hFile = 000000B4 (window)
004017A8|.E8 C9130000 call <jmp.&KERNEL32.ReadFile> ; \ReadFile
ReadFile函数从文件指针指定的位置读取数据。读操作完成后,文件指针将根据实际读出的数据自动进行调整,除非文件句柄是以OVERLAPPED属性值打开的。如果是以OVERLAPPED打开的I/O,应用程序就需要自己手动调整文件指针。
这个函数被设计成兼有同步和异步操作。ReadFileEx函数则设计成只支持异步操作,异步操作允许应用程序在读文件期间可以同时进行其他的操作。
函数原型:
BOOL ReadFile(
HANDLE hFile, // handle to file
LPVOID lpBuffer, // data buffer
DWORD nNumberOfBytesToRead, // number of bytes to read
LPDWORD lpNumberOfBytesRead, // number of bytes read
LPOVERLAPPED lpOverlapped // overlapped buffer
);
【Parameters】
1、hFile
文件句柄(必须具有GENERIC_READ访问权限)。
在Windows NT/2000/XP平台上:对于异步读操作,hFile可以是由CreateFile函数以FILE_FLAG_OVERLAPPED方式打开的任何句柄,或者一个由socket或accept函数返回的socket句柄。
在Windows 95/98/Me平台上:对于邮槽、命名管道和磁盘文件不能使用异步读操作。
2、lpBuffer
用来接收从文件中读出的数据的缓冲区指针。
3、nNumberOfBytesToRead
指明要读的字节总数。
4、lpNumberOfBytesRead
一个变量指针,用来存储实际传输的字节总数。ReadFile在做所有事情(包括错误检查)之前,先将这个值赋为0。当ReadFile从一个命名管道上返回TRUE时这个参数为0,说明消息管道另一端调用WriteFile时设置的nNumberOfBytesToWrite 参数为0。
在Windows NT/2000/XP平台上:如果lpOverlapped 为NULL,则lpNumberOfBytesRead不能为NULL。如果lpOverlapped 不是NULL,lpNumberOfBytesRead可以设为NULL。如果是一个overlapped形式的读操作,我们可以动用GetOverlappedResult函数来获得传输的实际字节数。如果hFile关联的是一个完成端口(I/O completion port),那么可以调用GetQueuedCompletionStatus函数来获得传输的实际字节数。
如果完成端口(I/O completion port)被占用,而你用的是一个用于释放内存的回调例程,对于lpOverlapped参数指向的OVERLAPPED结构体来说,为这个参数指定NULL可以避免重新分配内存时发生内存泄漏。内存泄漏会导致返回这个参数值时是一个非法值。
Windows 95/98/Me平台上:这个参数不允许为NULL。
5、lpOverlapped
一个指向OVERLAPPED结构体的指针。如果hFile是以FILE_FLAG_OVERLAPPED方式获得的句柄,这个结构是必须的,不能为NULL。(否则函数会在错误的时刻报告读操作已经完成了)。这时,读操作在由OVERLAPPED中Offset成员指定的偏移地址开始读,并且在实际完成读操作之前就返回了。在这种情况下,ReadFile返回FALSE,GerLastError报告从错误类型是ERROR_IO_PENDING。这允许调用进程继续其他工作直到读操作完成。OVERLAPPED结构中的事件将会在读操作完成时被使能。
如果hFile不是以FILE_FLAG_OVERLAPPED方式获得的句柄,并且lpOverlapped为NULL,读操作就从当前文件的开始位置读起,直到读操作完成ReadFile函数才能返回。
在Windows NT/2000/XP平台上:如果hFile不是以FILE_FLAG_OVERLAPPED方式获得的句柄,并且lpOverlapped不为NULL,则读操作在由OVERLAPPED中Offset成员指定的偏移地址开始读,直到读操作完成ReadFile函数才能返回。
在Windows 95/98/Me平台上:对于文件、磁盘、管道和邮槽的操作,这个参数必须为NULL。一个不为空的OVERLAPPED结构体指针将导致调用失败。Windows 95/98/Me平台只支持串行口和并行口的overlapped 读写。
【Return Values】
有如下任一种情况发生都会导致函数返回:(1)在管道另一端的写操作完成后(2)请求的字节数传输完毕(3)发生错误。
如果函数正确,返回非零。
如果返回值是非零,但接收的字节数是0,那么可能是文件指针在读操作期间超出了文件的end位置。然而,如果文件以FILE_FLAG_OVERLAPPED方式打开,lpOverlapped 参数不为NULL,文件指针在读操作期间超出了文件的end位置,那么返回值肯定是FALSE,GetLastError返回的错误是ERROR_HANDLE_EOF。
【Remarks】
如果文件的一部分被另一个进程锁定,而当前进程试图重复锁定,那将会失败。
一个应用程序在读以FILE_FLAG_NO_BUFFERING方式打开的文件时要符合一定的条件。
(1)文件读的开始地址必须是扇区大小的整数倍。GetDiskFreeSpace函数可以取得扇区的大小。
(2)请求读的字节数也必须是扇区大小的整数倍。
(3)用于读写操作的Buffer地址必须按照扇区大小进行边界对齐。可以通过用VirtualAlloc 函数申请内存来做到。
在读操作期间试图访问相应的输入缓冲区,会导致读入到缓冲区的数据损坏。读操作完成之前,应用程序不能对这段输入缓冲区做任何操作(包括读、写、重新分配内存,释放内存等)。
ReadFile可以通过指向控制台输入对象的句柄将控制台的输入字符读出来。控制台的模式决定了ReadFile的具体行为。
如果一个命名管道正在以消息模式被读取,并且下一条消息比nNumberOfBytesToRead参数指定的长度还大,那么ReadFile将返回FALSE并且GetLastError返回错误为ERROR_MORE_DATA。剩下没读完的消息可能会被随后的ReadFile或PeckNamedPipe函数读出。
读取一个通信设备时,ReadFile的行为被当前的通信延时所支配,延时属性的设置和取得使用SetCommTimeouts和GetCommTimeouts函数。如果你设置延时属性失败,就会得到不可预知的结果。
如果ReadFile试图读取一个buffer太小的邮槽,将会返回FALSE并且GetLastError返回错误为ERROR_INSUFFICIENT_BUFFER 。
如果一个匿名的写管道句柄已经关闭,而ReadFile试图用响应的匿名权限读这个管道句柄,将返回FALSE并且
GetLastError返回错误为ERROR_BROKEN_PIPE。
每当有太多的异步I/O请求得不到响应,ReadFile就会失败,并返回ERROR_INVALID_USER_BUFFER或ERROR_NOT_ENOUGH_MEMORY的错误。
在同步和异步两种情况下,ReadFile中检测EOF(文件结尾边界)的代码是不同的。当一个同步读操作到达文件结尾时,ReadFile返回TRUE,并设置*lpNumberOfBytesRead 为0 。异步读操作会在开始调用的读操作中或者随后的其他异步操作中突然遇到文件结尾。(1)如果EOF在ReadFile期间被检测到,将会返回FALSE,且 GetLastError返回错误描述 ERROR_HANDLE_EOF。(2)如果EOF在随后的其他异步操作中被检测到,则类似GetOverlappedResult 等试图获取操作结果的函数返回FALSE,且 GetLastError返回错误描述ERROR_HANDLE_EOF。
为了取消未响应的异步I/O操作,用CancelIo函数。这个函数只能取消由调用进程对特定句柄进行的操作。被取消的I/O操作将被描述为ERROR_OPERATION_ABORTED。
如果你正试图从并不存在的软驱中读数据,系统会弹出消息框提示你重新操作。为了阻止系统的消息框,调用函数SetErrorMode,参数设置为SEM_NOOPENFILEERRORBOX。 只有无尽的膜拜/:good 本帖最后由 whypro 于 2010-5-22 23:53 编辑
LocalAlloc,VirtualAlloc,malloc,new的异同
1. 首先我们来看HeapAlloc:
MSDN上的解释为:HeapALloc是从堆上分配一块内存,且分配的内存是不可移动的(即如果没有连续的空间能满足分配的大小,程序不能将其他零散的 空间利用起来,从而导致分配失败),该分配方法是从一指定地址开始分配,而不像GloabalAlloc是从全局堆上分配,这个有可能是全局,也有可能是 局部。函数原型为:
LPVOID
HeapAlloc(
HANDLE hHeap,
DWORD dwFlags,
SIZE_T dwBytes
);
hHeap是进程堆内存开始位置。
dwFlags是分配堆内存的标志。包括HEAP_ZERO_MEMORY,即使分配的空间清零。
dwBytes是分配堆内存的大小。
其对应的释放空间函数为HeapFree。
2. 再看GlobalAlloc:该函数用于从全局堆中分配出内存供程序使用,函数原型为:
HGLOBAL GlobalAlloc(
UINT uFlags,
SIZE_T dwBytes
);
uFlags参数含义
GHND GMEM_MOVEABLE和GMEM_ZEROINIT的组合
GMEM_FIXED 分配固定内存,返回值是一个指针
GMEM_MOVEABLE 分配活动内存,在Win32中,内存块不能在物理内存中移动,但能在默认的堆中移动。返回值是内存对象的句柄,用函数GlobalLock可将句柄转化为指针
GMEM_ZEROINIT 将内存内容初始化为零
GPTR GMEM_FIXED和GMEM_ZEROINIT的组合
一般情况下我们在编程的时候,给应用程序分配的内存都是可以移动的或者是可以丢弃的,这样能使有限的内存资源充分利用,所以,在某一个时候我们分配的那块 内存的地址是不确定的,因为他是可以移动的,所以得先锁定那块内存块,这儿应用程序需要调用API函数GlobalLock函数来锁定句柄。如下: lpMem=GlobalLock(hMem); 这样应用程序才能存取这块内存。所以我们在使用GlobalAllock时,通常搭配使用GlobalLock,当然在不使用内存时,一定记得使用 GlobalUnlock,否则被锁定的内存块一直不能被其他变量使用。
GlobalAlloc对应的释放空间的函数为GlobalFree。
3. LocalAlloc:该函数用于从局部堆中分配内存供程序使用,函数原型为:
HLOCAL LocalAlloc(
UINT uFlags,
SIZE_T uBytes
);
参数同GlobalAlloc。
在16位Windows中是有区别的,因为在16位windows用一个全局堆和局部堆来管理内存,每一个应用程序或dll装入内存时,代码段被装入全局 堆,而系统又为每个实例从全局堆中分配了一个64kb的数据段作为该实例的局部堆,用来存放应用程序的堆栈和所有全局或静态变量。而 LocalAlloc/GlobalAlloc就是分别用于在局部堆或全局堆中分配内存。
由于每个进程的局部堆很小,所以在局部堆中分配内存会受到空间的限制。但这个堆是每个进程私有的,相对而言分配数据较安全,数据访问出错不至于影响到整个系统。
而在全局堆中分配的内存是为各个进程共享的,每个进程只要拥有这个内存块的句柄都可以访问这块内存,但是每个全局内存空间需要额外的内存开销,造成分配浪费。而且一旦发生严重错误,可能会影响到整个系统的稳定。
不过在Win32中,每个进程都只拥有一个省缺的私有堆,它只能被当前进程访问。应用程序也不可能直接访问系统内存。所以在Win32中全局堆和局部堆都 指向进程的省缺堆。用LocalAlloc/GlobalAlloc分配内存没有任何区别。甚至LocalAlloc分配的内存可以被 GlobalFree释放掉。所以在Win32下编程,无需注意Local和Global的区别,一般的内存分配都等效于 HeapAlloc(GetProcessHeap(),...)。
LocalAlloc对应的释放函数为LockFree。
4. VirtualAlloc:该函数的功能是在调用进程的虚地址空间,预定或者提交一部分页,如果用于内存分配的话,并且分配类型未指定MEM_RESET,则系统将自动设置为0;其函数原型:
LPVOID VirtualAlloc(
LPVOID lpAddress, // region to reserve or commit
SIZE_T dwSize, // size of region
DWORD flAllocationType, // type of allocation
DWORD flProtect // type of access protection
);
VirtualAlloc可以通过并行多次调用提交一个区域的部分或全部来保留一个大的内存区域。多重调用提交同一块区域不会引起失败。这使得一个应用程 序保留内存后可以随意提交将被写的页。当这种方式不在有效的时候,它会释放应用程序通过检测被保留页的状态看它是否在提交调用之前已经被提交。
VirtualAlloc对应的释放函数为VirtualFree。
5.Malloc:malloc与free是C++/C语言的标准库函数,可用于申请动态内存和释放内存。对于非内部数据类型的对象而言,光用 malloc/free无法满足动态对象的要求。对象在创建的同时要自动执行构造函数,对象在消亡之前要自动执行析构函数。由于malloc/free是 库函数而不是运算符,不在编译器控制权限之内,不能够把执行构造函数和析构函数的任务强加于malloc/free。
6.New:new/delete是C++的运算符。可用于申请动态内存和释放内存。C++语言需要一个能完成动态内存分配和初始化工作的运算符new, 以一个能完成清理与释放内存工作的运算符delete。注意new/delete不是库函数。C++程序经常要调用C函数,而C程序只能用malloc /free管理动态内存。new 是个操作符,和什么"+","-","="...有一样的地位.
malloc是个分配内存的函数,供你调用的.
new是保留字,不需要头文件支持.
malloc需要头文件库函数支持.new 建立的是一个对象,
malloc分配的是一块内存.
new建立的对象你可以把它当成一个普通的对象,用成员函数访问,不要直接访问它的地址空间
malloc分配的是一块内存区域,就用指针访问好了,而且还可以在里面移动指针.
内存泄漏对于malloc或者new都可以检查出来的,区别在于new可以指明是那个文件的那一行,而malloc没有这些信息。new可以认为是malloc加构造函数的执行。new出来的指针是直接带类型信息的。而malloc返回的都是void指针。 看着有点迷糊哈,
页:
[1]
2