这是csdsjkk写的外壳,并且还提供了源码
壳作者自己的说明:
////////////////////////////////////////////
简单说一下壳采用的几种反跟踪调试的方法:
双进程、
多线程(解码、检测调试、检测api断点)、
iat变形(用importrec自动跟踪可以抓出来)、
自创的花指令等
说实话,我自己跟起都很费力
经过对源码的跟踪和分析,明白了一些
为了帮助更多的人学习这个源码所用的技术以及得到高手的指点和纠正
特附上了解读这个源码后加上的注释,不一定是正确,如果错了,请大家指出来
以免误导初学者
由于源码很长,只能注释一部分,但也一定是最详细的.
注:有API的地方一般不做注释,因为很好理解
start:
; int 3
jmp st1
invoke LoadLibrary,addr shell32
invoke GetProcAddress,eax,800000e9h
; invoke VirtualAlloc,0h,10000h,MEM_RESERVE,PAGE_READWRITE
; invoke VirtualQuery,eax,addr mbi,sizeof MEMORY_BASIC_INFORMATION
; invoke VirtualAlloc,0h,10000h,MEM_COMMIT,PAGE_READWRITE
; invoke VirtualAlloc,0h,10000h,MEM_COMMIT,PAGE_READWRITE
; invoke VirtualProtect,3e0000h,10,PAGE_READONLY,addr temp1
mov esi,400000h
@@:
invoke VirtualQuery,esi,addr mbi,sizeof MEMORY_BASIC_INFORMATION
; cmp dword ptr mbi[10h],10000h
; je @f
mov esi,dword ptr mbi[0] ;MEMORY_BASIC_INFORMATION.BaseAddress]
add esi,dword ptr mbi[0ch] ;MEMORY_BASIC_INFORMATION.RegionSize]
cmp eax,0
jne @b
@@:
mov eax,dword ptr mbi[0]
; invoke VirtualAlloc,eax,10h,MEM_COMMIT,PAGE_READWRITE
cmp eax,0
jne @f
invoke VirtualQuery,3e0000h,addr mbi,sizeof MEMORY_BASIC_INFORMATION
; invoke VirtualAlloc,0,10h,MEM_COMMIT,PAGE_READWRITE
invoke VirtualQuery,3e0000h,addr mbi,sizeof MEMORY_BASIC_INFORMATION
@@:
st1:
invoke VirtualProtect,addr entry,key_size,PAGE_READWRITE,addr temp1 ;改变内存页的保护属性
invoke GetTickCount
push eax
invoke GetTickCount
pop edx
mul edx
mov dword ptr dc_edit+1,eax ;用获得MS级的时间作为随机数
mov dword ptr ec_edit+1,eax
call calc_checksum ;制作两个代码的校验标志(0-所有代码中数据的字节和)
invoke GetTickCount
push eax
invoke GetTickCount
pop edx
mul edx
mov decode_key,eax ;用两个随机数相乘的值作为解码值.
not eax
mov rnd,eax
mov esi,offset _ok
mov edi,offset __ok
mov eax,decode_key
@@: ;把代码2编码,也就是加密
cmp esi,edi
jae @f ;JAE/JNB CF=0 时跳转
xor [esi],al
inc eax
ror eax,7 ;eax循环右移>>7
inc esi
jmp @b
@@:
invoke GetCommandLine
call trans ;分解命令行
invoke CreateFile,addr fname1,GENERIC_READ,FILE_SHARE_READ,0,OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL,0
cmp eax,INVALID_HANDLE_VALUE
je error1
mov hfile1,eax
invoke CreateFile,addr fname2,GENERIC_WRITE,0,0,CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL,0
cmp eax,INVALID_HANDLE_VALUE
je error3
mov hfile3,eax
invoke GetFileSize,hfile1,0
mov fsize1,eax
invoke VirtualAlloc,0,fsize1,MEM_COMMIT,PAGE_READWRITE
mov pt1,eax
invoke ReadFile,hfile1,pt1,fsize1,addr temp1,0
mov ebx,pt1
mov esi,[ebx+3ch] ;IMAGE_DOS_HEADER.e_lfanew
cmp flag_clear_boundimport,0
je @f
lea eax,[ebx+esi+0d0h] ;IMAGE_DIRECTORY_ENTRY_IAT.值为12
mov dword ptr [eax],0
mov dword ptr [eax+4],0 ;清空这个表
@@:
cmp flag_clear_load_config,0 ;IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT,值为11
je @f
lea eax,[ebx+esi+0c8h] ;清空这个表(一般来说这两个表都不用.)
@@:
mov dword ptr [eax],0
mov dword ptr [eax+4],0
@@:
mov eax,[ebx+esi+34h] ;指向ImageBase地址(RVA)
mov image_base,eax
push esi
lea esi,[ebx+esi+78h] ;IMAGE_DIRECTORY_ENTRY_EXPORT,指向导出表
mov edi,offset rva_table ;DIRECTORY表
mov ecx,80h
call move_memory ;调整各个表
pop esi
cmp flag_add_section,0 ;标志
je @f
add word ptr [ebx+esi+6],1
@@:
mov eax,0
xchg eax,[ebx+esi+80h] ;IMAGE_DIRECTORY_ENTRY_IMPORT,指向导入表
mov iat_offs,eax ;保存IAT,然后清空IAT
mov eax,0
xchg eax,[ebx+esi+84h]
mov iat_size,eax
mov eax,0
xchg eax,[ebx+esi+0a0h] ;IMAGE_DIRECTORY_ENTRY_BASERELOC,指向重定位表
mov reloc_offs,eax ;同样保存后清空
mov eax,0
xchg eax,[ebx+esi+0a4h]
mov reloc_size,eax
lea edi,[esi+0f8h] ;第一个节表
movzx eax,word ptr [ebx+esi+6] ;保存节的数目
mov temp1,eax
st2:
dec temp1
jz st3
jmp @f
cmp dword ptr [ebx+edi+0ch],0 ;;;;;;
je st3
cmp dword ptr [ebx+edi+08h],0 ;;;;;;
je st3
@@:
jmp st24
mov eax,dword ptr [ebx+esi+0a8h]
cmp eax,0
je @f
add eax,dword ptr [ebx+esi+0ach]
cmp eax,dword ptr [ebx+edi+0ch]
jb @f
mov eax,dword ptr [ebx+edi+0ch]
add eax,dword ptr [ebx+edi+8]
cmp eax,dword ptr [ebx+esi+0a8h]
ja st25
@@:
mov eax,dword ptr [ebx+esi+88h]
cmp eax,0
je @f
add eax,dword ptr [ebx+esi+8ch]
cmp eax,dword ptr [ebx+edi+0ch]
jb @f
mov eax,dword ptr [ebx+edi+0ch]
add eax,dword ptr [ebx+edi+8]
cmp eax,dword ptr [ebx+esi+88h]
ja st25
@@:
mov eax,dword ptr [ebx+esi+0c0h]
cmp eax,0
je @f
add eax,dword ptr [ebx+esi+0c4h]
cmp eax,dword ptr [ebx+edi+0ch]
jb @f
mov eax,dword ptr [ebx+edi+0ch]
add eax,dword ptr [ebx+edi+8]
cmp eax,dword ptr [ebx+esi+0c0h]
ja st25
@@:
st24:
mov eax,[ebx+edi+8] ;VirtualSize 节区大小
cmp eax,[ebx+edi+10h] ;SizeOfRawData 判断是否超过了对齐粒度
jbe @f
mov eax,[ebx+edi+10h]
@@:
mov ecx,[ebx+edi+14h] ;PointerToRawData 在文件中的偏移
;;;;;;add ecx,ebx
mov edx,[ebx+edi+0ch] ;VirtualAddress 节区的RVA
sub ecx,edx ; ;节区的RVA对应的偏移量
invoke encode,edx,eax,ebx,ecx ;对内存中的IAMAGE编码(加密被加壳的程序)
st25:
add edi,28h ;跳到下一个节(对每一节即代码,数据,资源都加密)
jmp st2
st3: ;各节的加密已经完成了
push edi
lea eax,[esi+0f8h] ;重新计算节的数目(最后节的节尾-起始节的节头)/节长
sub edi,eax
mov edx,0
mov eax,edi
mov edi,28h
div edi
mov number_of_section,eax
pop edi
cmp flag_add_section,0 ;引入的标志
jne st6
mov eax,fsize1
sub eax,[ebx+edi+14h]
test eax,00000fffh
jz @f
add eax,1000h
@@:
and eax,0fffff000h
push eax
add eax,key_size
test eax,00000fffh
jz @f
add eax,1000h
@@:
and eax,0fffff000h
mov [ebx+edi+08h],eax
mov [ebx+edi+10h],eax
or dword ptr [ebx+edi+24h],0a0000020h ;mov dword ptr [ebx+edi+24h],0e0000040h
pop ecx
mov eax,[ebx+edi+0ch]
add eax,ecx
add eax,offset entry
sub eax,offset entry
xchg [ebx+esi+28h],eax
mov shell_eip,eax
jmp st7
st6:
mov eax,key_size ;代码长度
test eax,00000fffh ;测试是否对齐(1000h*n)
jz @f
add eax,1000h
@@:
and eax,0fffff000h
mov [ebx+edi+8],eax ;写入新节的内容
sub edi,28h
mov eax,[ebx+edi+0ch]
add eax,[ebx+edi+8]
test eax,00000fffh
je @f
add eax,1000h
@@:
and eax,0fffff000h ;计算位置
add edi,28h
mov [ebx+edi+0ch],eax ;VirtualAddress 节区的RVA地址
mov eax,key_size
test eax,00000fffh
jz @f
add eax,1000h
@@:
and eax,0fffff000h
mov [ebx+edi+10h],eax ;SizeOfRawData 在文件中对齐后的尺寸
mov eax,fsize1
test eax,00000fffh
je @f
add eax,1000h
@@:
and eax,0fffff000h
mov [ebx+edi+14h],eax ; PointerToRawData 在文件中的偏移
mov dword ptr [ebx+edi+24h],0e0000020h ;Characteristics 节的属性
mov eax,[ebx+edi+0ch]
add eax,offset entry
sub eax,offset entry
xchg [ebx+esi+28h],eax ;更改AddressOfEntryPoint
mov shell_eip,eax ;入口
st7:
mov eax,[ebx+edi+0ch] ;新节区的VirtualAddress 节区的RVA地址
add eax,[ebx+edi+8] ;VirtualSize 新节区的尺寸
test eax,00000fffh ;测试有没对齐
je @f
add eax,1000h
@@:
and eax,0fffff000h
mov [ebx+esi+50h],eax ;更改SizeOfImage 内存中整个PE映像尺寸
;所有的文件头中要修改的部分都修改了
mov eax,decode_key ;解码键
xor shell_eip,eax
lea esi,entry00
mov ecx,__ok - entry00
@@:
not byte ptr [esi] ;加密entry00(解码加到被加壳文件的代码)的代码
inc esi
loop @b
mov flag_reentry,0 ;flag_reentry=0表示已加密
invoke WriteFile,hfile3,pt1,fsize1,addr temp1,0 ;写到新文件中(不过还是原文件的大小)
cmp flag_add_section,0 ;标志
jne st75
mov eax,fsize1
sub eax,[ebx+edi+14h]
mov ecx,1000h
sub ecx,eax
jns st71
@@:
add ecx,1000h
js @b
st71:
mov eax,ecx
jmp st8
st75:
mov eax,fsize1 ;新文件的长度
test eax,00000fffh ;有无对齐
je @f
add eax,1000h
@@:
and eax,0fffff000h
sub eax,fsize1
st8:
lea ecx,_fill
invoke WriteFile,hfile3,ecx,eax,addr temp1,0 ;写入_fill处的数据.现在是空值(加大1K)
mov eax,key_size
test eax,00000fffh
jz @f
add eax,1000h
@@:
and eax,0fffff000h
invoke WriteFile,hfile3,addr entry,eax,addr temp1,0 ;写入entry代码
invoke CloseHandle,hfile1 ;关闭这两个文件
invoke CloseHandle,hfile3
invoke VirtualFree,pt1,0,MEM_RELEASE
invoke CreateFile,addr fname2,GENERIC_READ + GENERIC_WRITE,0,0,OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL,0
cmp eax,INVALID_HANDLE_VALUE
je error3
mov hfile1,eax
invoke GetFileSize,hfile1,0 ;得到文件的大小,用于后面读出全部内容
mov fsize1,eax
invoke VirtualAlloc,0,fsize1,MEM_COMMIT,PAGE_READWRITE ;分配虚拟内存
mov pt1,eax
invoke ReadFile,hfile1,pt1,fsize1,addr temp1,0 ;把文件读入缓冲区pt1中
mov ecx,fsize1
mov edi,pt1
uu1:
cmp ecx,8 ;在文件中有无'LiNr'和'VoTa'字符串
jb nofound
cmp dword ptr [edi],'LiNr' ;'rNiL' ;加壳后的标志'rNiLaToV'
jne uu2
cmp dword ptr [edi+4],'VoTa' ;'aToV'
je found
uu2:
inc edi
loop uu1
jmp nofound
found: ;已加壳了
add edi,8
sub edi,pt1 ;获得标志'rNiLaToV'的偏移量
mov ebx,pt1
mov esi,[ebx+3ch] ;获得PE头位置
movzx ecx,word ptr [ebx+esi+6] ;扩展为双字NumberOfSections
mov eax,[ebx+esi+74h] ; NumberOfRvaAndSizes
shl eax,3 ;NumberOfRvaAndSizes*8(每个IMAGE_DATA_DIRECTORY 8字节)
lea edx,[ebx+esi+78h] ;IMAGE_DIRECTORY_ENTRY_EXPORT
add edx,eax ;得到节表的位置
uu3:
cmp dword ptr [edx+14h],edi ;PointerToRawData 在文件中的偏移
ja s_nt
mov eax,[edx+8] ;VirtualSize 节区的尺寸
add eax,[edx+14h] ;得到代码段的位置
cmp eax,edi
jbe s_nt ;超过的话,执行下面的代码
mov eax,edi ;文件尾
sub eax,[edx+14h] ;算出所余的空间
add eax,[edx+0ch] ;VirtualAddress 节区的RVA地址(把这个作为导入表的地址)
mov [ebx+esi+80h],eax ;IMAGE_DIRECTORY_ENTRY_IMPORT的VirtualAddress
mov dword ptr [ebx+esi+84h],14h ;IMAGE_DIRECTORY_ENTRY_IMPORT的isize(数据块的长度)
lea ecx,[eax+size1] ;
mov [ebx+edi+0ch],ecx ;VirtualAddress 节区的RVA地址
lea ecx,[eax+size1+size2] ;加上DLL的长度后的RVA
mov [ebx+edi+size1+size2+size3],ecx ;在原来的函数表后面再填上这个RVA
lea ecx,[eax+size1+size2+size3] ;算出新的RVA
mov [ebx+edi+10h],ecx ;SizeOfRawData 在文件中对齐后的尺寸
jmp found1
s_nt:
add edx,28h ;没超过的话,取下个节
loop uu3
jmp nofound
found1:
invoke SetFilePointer,hfile1,0,0,FILE_BEGIN
invoke WriteFile,hfile1,pt1,fsize1,addr temp1,0 ;重写,更改了IAT了.原来全是0
nofound:
invoke CloseHandle,hfile1 ;关闭原文件
invoke VirtualFree,pt1,0,MEM_RELEASE ;释放分配的内存
error1:
error2:
error3:
exit0:
invoke ExitProcess,0 ;退出进程,程序终止
_fill db 1000h dup (0) ;填充数据0
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!