小弟最近在学习加壳,但是太过愚钝,感觉写个加壳还真难,自己试着写了一个但是有很多错误,哪位大哥能帮我看看
我权限不够, 不能上传附件,加贴上来了
PeShell.asm
; 数据段
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
.data
FilterString db "PE可执行文件(*.exe)", 0, "*.exe", 0, 0
WrongMessage db "wrong", 0
WrongMessage01 db "Some thing is wrong!", 0
RightMessage DB "ok",0
RightMessage01 DB "ok!",0
;********************各种消息*************
M_FileOpenErr DB "错误!文件打开失败!",0
M_FileIsNotExe DB "错误!不是可执行文件!",0
M_FileIsNotPe DB "错误!不是标准的PE格式!",0
M_ReadFileOK DB "文件读入完成.",0
M_VirtualAllocErr DB "申请使用内存失败.",0
.data?
hInst dd ?
Openfilename OPENFILENAME <?>
FileName db MAXSIZE dup(?)
hFile dd ?
pMap dd ?
;OEP dd ?
PeFileAlign dd ?
PeSectionAlign dd ?
PeImageBase dd ?
PeImageSize dd ?
FileSize dd ?
ShellBase dd ?
ShellSize dd ?
NumberOfBytesRW dd ?
ShellStart0Size dd ?
ShellStartSize dd ?
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 代码段
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
.code
include shell.asm
start:
;***************打开文件**********************
invoke GetModuleHandle, NULL ;获取当前文件句柄
mov hInst, eax
invoke RtlZeroMemory, OFFSET FileName, MAXSIZE ;清空文件名的缓存
mov Openfilename.lStructSize, SIZEOF Openfilename ;获取文件结构的大小是必须的
push hInst
pop Openfilename.hInstance
mov Openfilename.lpstrFilter, OFFSET FilterString ;文件类型字符串 "*.TXT;*.DOC;*.BAK"
mov Openfilename.lpstrFile, OFFSET FileName ;文件名全路径
mov Openfilename.nMaxFile , MAXSIZE
mov Openfilename.Flags, OFN_FILEMUSTEXIST + OFN_PATHMUSTEXIST + \
OFN_EXPLORER + OFN_HIDEREADONLY
invoke GetOpenFileName, addr Openfilename ;获取一个打开文件的对话框
test eax, eax
jz wrong
;***************将文件读入内存****************
invoke CreateFile, addr FileName, GENERIC_READ+GENERIC_WRITE, \
0,0, 3, FILE_ATTRIBUTE_NORMAL,NULL
cmp eax, INVALID_HANDLE_VALUE
jz OpenFileErr
mov hFile, eax
;***************判断是否是PE文件**************
invoke GetFileSize, hFile, addr NumberOfBytesRW
mov FileSize, eax
add eax, 2000h
invoke VirtualAlloc, NULL, eax, MEM_COMMIT, PAGE_READWRITE
mov pMap, eax ;缓冲区首地址
invoke ReadFile, hFile, pMap, FileSize, addr NumberOfBytesRW, NULL
mov edx, pMap
cmp word ptr[edx], 'ZM' ;是否是EXE文件
jnz FileIsNotEXE
mov eax, dword ptr[edx + 3ch]
add edx, eax ;是否是PE文件
cmp word ptr[edx], 'EP'
jnz FileIsNotPE
;***************读取文件头和文件区块相关信息,最终计算出文件对齐粒度后的文件大小,获取文件尾**
assume edx : ptr IMAGE_NT_HEADERS ;指向PE文件头
;mov PeHeadBase, edx
mov eax, dword ptr[edx].OptionalHeader.ImageBase ;程序默认装入的基址RVA
mov PeImageBase, eax
mov eax, dword ptr[edx].OptionalHeader.FileAlignment ;文件中快对齐粒度
mov PeFileAlign, eax
mov eax, dword ptr[edx].OptionalHeader.SectionAlignment ;内存中的块对齐粒度
mov PeSectionAlign, eax
mov eax, dword ptr[edx].OptionalHeader.SizeOfImage ;内存中整个PE映像尺寸
mov PeImageSize, eax
invoke GetIntegral, FileSize, PeFileAlign ;对齐文件大小
mov FileSize, eax
;invoke GetIntegral, PeImageSize,PeSectionAlign
;mov PeImageSize, eax
;mov dword ptr [edx].OptionalHeader.SizeOfImage, eax
mov edi, pMap
add edi, FileSize ;指向了文件尾
mov ShellBase, edi ;存放的附加块的首地址,即指向文件尾
mov ShellSize, ShellEnd0 - ShellStart0
add ShellSize, ShellEnd - ShellStart
;***************以下部分是保存外壳部分代码************************
;invoke VirtualAlloc, NULL, 20000h, MEM_COMMIT, PAGE_READWRITE
;mov ShellBufferMap,eax ;用于暂时保存真正的外壳部分
;***************保存外壳引导段(第一段)******
mov ecx, ShellEnd0 - ShellStart0
mov ShellStart0Size, ecx
lea esi, ShellStart0
mov edi, ShellBase
rep movsb
;***************保存外壳第二段****************
mov edi, ShellBase
add edi, ShellStart0Size
mov ShellBase, edi
lea esi, ShellStart ;外壳第二段首地址
mov ecx, ShellEnd - ShellStart
mov ShellStartSize, ecx
rep movsb
;***************保存新的入口点EPO和输入表地址*
mov eax, dword ptr[edx].OptionalHeader.AddressOfEntryPoint
mov ebx, ShellBase
add ebx, OEP - ShellStart
mov dword ptr[ebx], eax
;***************修正外壳输入表****************
mov eax, PeImageSize
add eax, ImportTable - ShellStart0 ;得到外壳输入表的偏移
mov ebx, ShellBase
add dword ptr[ebx + (ImportTable - ShellStart0)], eax
add dword ptr[ebx + (AppImpRVA1 - ShellStart0)], eax
add dword ptr[ebx + (AppImpRVA2 - ShellStart0)], eax
add dword ptr[ebx + (AddrFirst - ShellStart0)], eax
add dword ptr[ebx + (AddrSecond - ShellStart0)], eax
add dword ptr[ebx + (AddrThird - ShellStart0)], eax
;***************增加一个节表*******************
movzx ebx, word ptr[edx].FileHeader.SizeOfOptionalHeader
;ModifySectionCharact: ;修改个区块的属性
; or dword ptr [eax+24h], 0c0000000h
; add eax, 28h
; loop ModifySectionCharact
add ebx, edx
add ebx, 18h
movzx eax, word ptr[edx].FileHeader.NumberOfSections
add ebx, eax ;ebx指向节表尾即新块表的起点
mov edi, ebx
xor eax, eax
mov ecx, 28h
rep stosb ;添加一个新的块表
add ShellSize, 28h
mov dword ptr[ebx], 'gcc.'
invoke GetIntegral, ShellSize, PeSectionAlign
mov dword ptr[ebx + 8h], eax ;映像大小
mov eax, PeImageSize
mov dword ptr[ebx + 0ch], eax ;映像偏移
invoke GetIntegral, ShellSize, PeFileAlign
mov dword ptr[ebx + 10h], eax ;文件大小
add FileSize, eax ;加壳后文件的大小
mov eax, dword ptr[ebx - 14h]
add eax, dword ptr[ebx - 18h]
invoke GetIntegral, eax, PeFileAlign
mov dword ptr[ebx + 14h], eax ;文件偏移
mov dword ptr[ebx + 24h], 0c0000040h
inc word ptr[edx].FileHeader.NumberOfSections ;区块数加一
;***************修正文件头的一些资料(包括输入表)
mov eax, PeImageSize
mov dword ptr[edx].OptionalHeader.AddressOfEntryPoint, eax ;修改入口点EntryPoint
invoke GetIntegral, ShellSize, PeSectionAlign
add eax, PeImageSize
mov dword ptr[edx].OptionalHeader.SizeOfImage, eax ;修改映像大小
mov eax, PeImageSize
add eax, ImportTable - ShellStart0 ;修改输入表
mov dword ptr[edx].OptionalHeader.DataDirectory[SIZEOF IMAGE_DATA_DIRECTORY].VirtualAddress, eax
;**********清楚文件头中一些不必要的数据
mov dword ptr[edx].OptionalHeader.DataDirectory[11 * SIZEOF IMAGE_DATA_DIRECTORY].VirtualAddress, 0h
mov dword ptr[edx].OptionalHeader.DataDirectory[11 * SIZEOF IMAGE_DATA_DIRECTORY].isize, 0h
;***************将外壳写入文件末尾************
invoke SetFilePointer, hFile, 0h, NULL, FILE_BEGIN
invoke WriteFile, hFile, pMap, FileSize, addr NumberOfBytesRW, NULL
test eax, eax
jz wrong
right:
invoke MessageBox, NULL, addr RightMessage01, addr RightMessage, MB_OK
invoke ExitProcess, NULL
wrong:
invoke MessageBox, NULL, addr WrongMessage01, addr WrongMessage, MB_OK
invoke ExitProcess, NULL
;**************************************************************
GetIntegral PROC SourceValue:DWORD, AlignmentValue:DWORD
push edx
push ecx
mov eax, SourceValue
mov ecx, AlignmentValue
xor edx, edx
div ecx
.if edx!=0
inc eax
.endif
xor edx, edx
mul AlignmentValue
pop ecx
pop edx
ret
GetIntegral endp
OpenFileErr:
invoke MessageBox, NULL, addr WrongMessage01, addr WrongMessage, MB_OK
invoke ExitProcess, NULL
FileIsNotEXE:
invoke MessageBox, NULL, addr WrongMessage01, addr WrongMessage, MB_OK
invoke ExitProcess, NULL
FileIsNotPE:
invoke MessageBox, NULL, addr WrongMessage01, addr WrongMessage, MB_OK
invoke ExitProcess, NULL
end start
---------------------------------------------------------------------
shell.asm
;************************外壳第一段*********************************************
ShellStart0:
push ebp ;ebp是基址指针寄存器
call next0
;手工构造外壳输入表(写入文件前要修正)
ImportTableBegin:
ImportTable dd AddrFirst - ImportTable
dd 0, 0
AppImpRVA1 dd DllName - ImportTable
AppImpRVA2 dd AddrFirst - ImportTable
dd 0, 0, 0, 0
AddrFirst dd FirstFunc - ImportTable
AddrSecond dd SecondFunc - ImportTable
AddrThird dd ThirdFunc - ImportTable
dd 0
DllName db 'KERNEL32.dll'
dw 0
FirstFunc dw 0
db 'GetProcAddress', 0
SecondFunc dw 0
db 'GetModuleHandleA', 0
ThirdFunc dw 0
db 'LoadLibraryA', 0
ImportTableEnd:
;外壳第一段主要用到的变量
;ShellBase dd 0 ;保存外壳压缩部分相对于ShellStart0的偏移地址
ShellPackSize dd 0 ;保存外壳压缩部分的原始大小
Virtualalloc db 'VirtualAlloc', 0
VirtualallocADDR dd 0
;TlsTableDB 18h dup(?) ;保存原始程序的TLS表
;***************获取ShellStart0的地址和导入外壳第二段
next0:
pop ebp
sub ebp, (ImportTable - ShellStart0) ;ebp保存的是ShellStart0的地址
lea esi, [ebp + (AddrSecond - ShellStart0)] ;指向KERNEL32.dll
push esi
call dword ptr[ebp + (AddrSecond - ShellStart0)] ;取得DLL句柄
lea esi, [ebp + (Virtualalloc - ShellStart0)]
push esi ;VirtualAlloc地址压入栈
push eax ;DLL句柄压入栈
call dword ptr[ebp + (AddrFirst - ShellStart0)] ;取得VirtualAlloc函数入口
mov dword ptr[ebp + (VirtualallocADDR - ShellStart0)], eax
push PAGE_READWRITE
push MEM_COMMIT
push dword ptr[ebp + (ShellPackSize - ShellStart0)]
push 0
call dword ptr[ebp + (VirtualallocADDR - ShellStart0)] ;调用VirtualAlloc函数申请内存空间用于存放外壳第二段
push eax ;缓冲区首地址
;如果外壳第二段没有压缩,则不必添加解压外壳第二段的部分的代码,那么上边的获取函数的代码也是没有意义的
push ebp ;保存第一段的基址,以便第二段中使用
jmp edx ;转到第二段继续执行
ShellEnd0:
ShellStart:
call $ + 5
pop edx
sub edx, 5h ;获取ShellStart的地址
pop ebp ;取出第一段的基地址
mov ecx, 3h ;好像没什么用耶?
lea esi, [ebp + (AddrFirst - ShellStart0)]
lea edi, [edx + (GetprocaddressADDR - ShellStart)]
MoveThreadAddr:
mov eax, dword ptr[esi]
mov dword ptr[edi], eax
add esi, 4h
add edi, 4h
loop MoveThreadAddr ;保存外壳输入表的3个函数入口地址备用
;此处省略了保存解压函数入口备用的代码
mov eax, dword ptr[ebp + (VirtualallocADDR - ShellStart0)]
mov dword ptr[edx + (S_VirtualallocADDR - ShellStart)], eax ;保存缓冲区函数入口地址备用
mov ebp, edx ;ebp保存的是ShellStart基址
push 0 ;参数为零表示当前文件
call dword ptr[ebp + (GetmulehandleADDR - ShellStart)]
mov dword ptr[ebp + (FileHandle - ShellStart)], eax ;取得当前文件句柄
;***********取一些函数的入口
lea esi, dword ptr[ebp + (Ker32DllName - ShellStart)]
push esi
call dword ptr[ebp + (GetmulehandleADDR - ShellStart)]
.if eax == 0
push esi
call dword ptr[ebp + (LoadlibraryADDR - ShellStart)]
.endif
mov esi, eax
lea ebx, dword ptr[ebp + (S_Virtualfree - ShellStart)]
push ebx ;第二个参数函数名指针
push esi ;第一个参数DLL句柄
call dword ptr[ebp + (GetprocaddressADDR - ShellStart)]
mov dword ptr[ebp + (S_VirtualfreeADDR - ShellStart)], eax ;保存函数VirtualFree的入口地址备用
;***********初始化原始程序的输入表(恢复原输入表)************
;mov eax, dword ptr[ebp + (S_IsProImpTable - ShellStart)]
;.if eax == 0 ;对转储钱的输入表的初始化(还原)
mov edi, dword ptr[ebp + (ImpTableAddr - ShellStart)] ;取出原输入表的的RVA
add edi, dword ptr[ebp + (FileHandle - ShellStart)] ;加上载入基址
GetNextDllAddr:
mov esi, dword ptr[edi + 0ch] ;指向DLL名字符串
.if esi == 0 ;为空表示所有的DLL都初始化完成
jmp AllDllFuncAddrGeted
.endif
add esi, dword ptr[ebp + (FileHandle - ShellStart)]
push esi
call dword ptr[ebp + (GetmulehandleADDR - ShellStart)] ;取DLL句柄
.if eax == 0 ;为0表示DLL未被载入
push esi
call dword ptr[ebp + (LoadlibraryADDR - ShellStart)] ;载入DLL
.endif
mov esi, eax ;保存Dll句柄
mov edx, dword ptr[esi] ;取OrginalFirstThunk的值
.if edx == 0 ;为0则用FirstThunk来代替
mov edx, dword ptr[edi + 10h]
.endif
add edx, dword ptr[ebp + (FileHandle - ShellStart)]
mov ebx, dword ptr[edi + 10h]
add ebx, dword ptr[ebp + (FileHandle - ShellStart)]
GetNextFuncAddr:
mov eax, dword ptr[edx] ;取得一个IMAGE_THUNK_DATA
.if eax == 0 ;如果为0表示此DLL中所有的函数初始化完成
jmp AllFuncAddrGeted
.endif
push ebx
push edx
cdq ;双字转为四字
.if edx == 0 ;判断IMAGE_THUNK_DATA最高位是否为0
add eax, 2h
add eax, dword ptr[ebp + (FileHandle - ShellStart)] ;EAX指向函数名
.else
and eax, 7ffffffh ;EAX为函数序号
.endif
push eax ;以函数名指针或函数序号为输入
push esi ;Dll句柄
call dword ptr[ebp + (GetprocaddressADDR - ShellStart)] ;取得函数入口
mov dword ptr[ebx], eax ;将入口地址写回FirstThunk指向的IMAGE_THUNK_DATA
pop edx
pop ebx
add edx, 4h
add ebx, 4h
jmp GetNextFuncAddr ;准备处理下一函数
AllFuncAddrGeted:
add edi, 14h
jmp GetNextDllAddr ;准备处理下一Dll
AllDllFuncAddrGeted:
;.else ;对转储后的输入表的初始化,略
;.endif
fuapfdw_finished:
;*************准备返回OEP***************
;mov dword ptr [ebp+(ShellImageBase-ShellStart)],ebp
mov eax,dword ptr [ebp+(OEP-ShellStart)]
add eax,dword ptr [ebp+(FileHandle-ShellStart)]
jmp eax ;转到原始程序执行
;以下部分为外壳程序总要用的变量
GetprocaddressADDR dd 0
GetmulehandleADDR dd 0
LoadlibraryADDR dd 0
S_VirtualallocADDR dd 0
FileHandle dd 0 ;放入程序执行句柄(载入基址)
S_IsProImpTable dd 0 ;放入输入表转储标志
ImpTableAddr dd 0 ;放入原始程序输入表地址
OEP dd 0 ;放入原始程序的入口RVA
Ker32DllName db 'KERNEL32.dll', 0
S_Virtualfree db 'VirtualFree', 0
S_VirtualfreeADDR dd 0
ShellEnd:
------------------------------------------------------
makefile
NAME = PeShell
OBJS = $(NAME).obj
LINK_FLAG = /subsystem:windows
ML_FLAG = /c /coff
$(NAME).exe: $(OBJS)
Link $(LINK_FLAG) $(OBJS)
.asm.obj:
ml $(ML_FLAG) $<
clean:
del *.obj
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课