21,497
社区成员




;注意masm编译,注意这个程序不能直接运行,编译完成后od加载code3之前运行一次是加密,运行完dump出来就是shellcode了!
.386p
.model flat, stdcall
.code
start:
assume fs:flat, gs:flat
;这里直接写代码
;------------------------B.j.H's split-line-----------------------------------
;首先建立一个在shellcode中需要用到的字符表他应该包括
;LoadLibraryA
;user32
;MessageBoxA
;ExitProcess
;B.J.H 当然为了弹出的Msgbox有个性可以添加这样一字符串,用来弹框框
;这些字符统统压入堆栈以便调用!
;所以我们代码的前面是
;------------------------B.j.H's split-line-----------------------------------
push 0048h ;B.J.H
push 2e4a2e42h ;
push 00737365h ;ExitProcess
push 636f7250h
push 74697845h
push 0041786fh ;MessageBoxA
push 42656761h
push 7373654dh
push 00323372h ;user32
push 65737500h
push 41797261h ;LoadLibraryA
push 7262694ch
push 64616f4ch
mov edx,esp ;保存字符串首地址
;上面是一个函数表!注意压入的字符格式,注意堆栈是倒过来的!慢慢调试可以感觉出来的!
;最后一据代码是保存字符串表的的入口(也就是首地址)
;------------------------B.j.H's split-line-----------------------------------
;获得Kernel的基地址(这一段很多地方都能找到!)不做解释了!
xor eax,eax
mov eax,dword ptr FS:[030h]
mov eax,DWORD PTR [eax+0ch]
mov esi,DWORD PTR [eax+01ch]
lodsd
mov eax,dword ptr [eax+8h]
;------------------------B.j.H's split-line-----------------------------------
;现在eax中存放的就是kernel32的基地址了!
;当处到这里我就不知道该怎么出来了!得到基地址到底有什么用呢!不知道!呵呵!时间久了!
;忙忙的阅读一些资料!自然而然的就懂了!...我不是那种一看资料就明白的那种具有潜质的大
;鸟,只是一点一点体会的小菜鸟!
;下面先写一个调用就是
;------------------------B.j.H's split-line-----------------------------------
push eax ;保存kernel32.dll基地址
push edx ;传入需要查询函数名地址
push 0ch ;函数名长度
call getapi ;调用函数得到函数入口
;------------------------B.j.H's split-line-----------------------------------
pop ebx ;弹出堆栈里的的函数名地址(是之前建立的)
add ebx,0dh ; 加上字符长度得到下一个需要使用的字符串的
push ebx ;参数"user32"
call eax ;调用LoadLibraryA("user32"),加载
;user32.dll,且eax中是从函数调用返回的user32的基地址
;------------------------B.j.H's split-line-----------------------------------
add ebx,07h ; 继续得到下一个需要使用的字符串。下面的几个调用不详细说了... ...
push ebx ;
push 0bh ;
call getapi ;调用getaddr函数获取MessageBoxA函数地址.
;------------------------B.j.H's split-line-----------------------------------
pop ebx ;
add ebx,018h ;
push 0h ;
push ebx ;
push ebx ;
push 0h ;
call eax ;调用MessageBoxA
;------------------------B.j.H's split-line-----------------------------------
mov edx,0ch ;
pop eax ;
sub ebx,edx ;
push ebx ;
push edx ;
call getapi ;调用getaddr函数获取exitprocess函数地址.
call eax ;调用exitprocess
;------------------------B.j.H's split-line-----------------------------------
;下面这个才是重点!(个人认为)
;写一个根据dll的基地址查找API函数入口 的函数方便调用
;函数说明:此函数包括两个传入参数,一个是基地址,另一个是需要查询的函数名字符串首地址
;------------------------B.j.H's split-line-----------------------------------
getapi:
mov ebx,eax ;eax存放的传入的基地址
add eax,03ch ;定位PE头位置地址
mov eax,dword ptr [eax] ;获得PE头偏移地址
add eax,ebx ;计算PE头VA
cmp dword ptr [eax],00004550h ;验证PE文件的合法性
jne Err ;不合法跳转
mov eax,dword ptr [eax+078h] ;导出表地址获得
add eax,ebx ;计算导出表入口VA
push eax ;保存导出表入口VA
mov ecx,eax ;令ecx指向导出表
mov ecx,dword ptr [ecx + 014h] ;找出导出表中函数个数作为循环值
mov eax,dword ptr [eax + 020h] ;找出函数名字符串地址表的偏移
add eax,ebx ;计算函数名字符串地址表的VA
push ebp ;保存ebp寄存器,寄存器不够用,借用一下,呵呵!
mov ebp,eax ;ebp保存函数名字符串地址表的VA
xor edx,edx ;edx清0用于存放函数索引
LoopFind:
push ecx; ;保存ecx 循环次数
mov eax,dword ptr[eax] ;获取函数名字符串偏移
add eax,ebx ;计算函数字符串VA
mov edi,eax ;把VA赋值给edi 准备比较
mov esi,dword ptr [esp + 014h] ;这个地方要算好了!函数内部有3个push,每个push为4个字节一个12个字节
;字符串地址放在call外部倒数第二个push(这个自己用控制好第几个参数是自己定的)
;也就是说从push 字符串地址到这个命令行之间有16个字节被push进入栈中。是不是就是+10H呢
;注意我们这里用了一个函数,不是jmp,函数调用的时候会有一个push eip用于返回!
;所以我们在计算的时候这个push不能忘,最后计算得到esp+14h中所放的就我们要查的函数名字符的地址
mov ecx,dword ptr [esp + 010h] ; 计算同上,这里是给ecx赋值,函数名的长度。ecx被改变了
;(后面,在loop命令之前要恢复的ecx,这个是计数器,硬性规定,呵呵!)
cld ;
repz cmpsb ;字符串比较 esi 和 edi 比较
jne FindNext ;比较,如果不是,则取下一个函数
add esp,4 ;找到往下执行 相当与pop ecx。无关紧要了!
mov eax,dword ptr [esp + 04h] ;恢复导出函数表入口
mov eax,dword ptr [eax + 01ch] ;获得函数地址表入口偏移
;我在这里缺少了一步,大多数情况不会有影响,具体的是哪一步,
;深究的同志可以看看关于函数导出表的函数查询方式
add eax,ebx ;计算函数地址表入口VA
shl edx,2 ;函数索引*函数地址(默认为4个字节) 鉴戒别人的,很妙的一招相当与乘以4
add eax,edx ;函数地址表基址+(函数索引)
mov eax,dword ptr[eax] ;获得函数入口地址偏移
add eax,ebx ;计算VA
jmp Found
FindNext:
inc edx ;计数器加一
add ebp,4h ;函数名字符串地址表的VA指向下一个函数名地址
mov eax,ebp ; 函数名地址的VA赋值到eax
pop ecx ;恢复ecx 循环次数
loop LoopFind ;loop 继续循环查找
Err:
xor eax,eax ;没有找到,或者出错,eax清理。
Found:
pop ebp ;恢复ebp
pop ecx ;不能再恢复eax了!弹到ecx好了!
ret 4 ; 返回 且 add esp 4
;这个是因为我们调用完函数时需要利用之前我们自己建立函数名字符表。
end start
7C80ACDF E8 0D6EFFFF call LoadLibraryExW
7C80ACD3 > 8BFF mov edi, edi
7C80ACD5 55 push ebp
7C80ACD6 8BEC mov ebp, esp
7C80ACD8 6A 00 push 0
7C80ACDA 6A 00 push 0
7C80ACDC FF75 08 push dword ptr [ebp+8]
7C80ACDF E8 0D6EFFFF call LoadLibraryExW
7C80ACE4 5D pop ebp
7C80ACE5 C2 0400 retn 4
7C80AC28 > 8BFF mov edi, edi
7C80AC2A 55 push ebp
7C80AC2B 8BEC mov ebp, esp
7C80AC2D 51 push ecx
7C80AC2E 51 push ecx
7C80AC2F 53 push ebx
7C80AC30 57 push edi
7C80AC31 8B7D 0C mov edi, dword ptr [ebp+C]
7C80AC34 BB FFFF0000 mov ebx, 0FFFF
7C80AC39 3BFB cmp edi, ebx
7C80AC3B 0F86 571C0000 jbe 7C80C898
7C80AC41 57 push edi
7C80AC42 8D45 F8 lea eax, dword ptr [ebp-8]
7C80AC45 50 push eax
7C80AC46 FF15 8812807C call dword ptr [<&ntdll.RtlInitString>] ; ntdll.RtlInitString
7C80AC4C 8D45 0C lea eax, dword ptr [ebp+C]
7C80AC4F 50 push eax
7C80AC50 6A 00 push 0
7C80AC52 8D45 F8 lea eax, dword ptr [ebp-8]
7C80AC55 50 push eax
7C80AC56 6A 00 push 0
7C80AC58 FF75 08 push dword ptr [ebp+8]
7C80AC5B E8 C2ECFFFF call 7C809922
7C80AC60 50 push eax
7C80AC61 E8 B7FFFFFF call <jmp.&ntdll.LdrGetProcedureAddress>
7C80AC66 85C0 test eax, eax
7C80AC68 0F8C DE830000 jl 7C81304C
7C80AC6E 6A 00 push 0
7C80AC70 FF75 08 push dword ptr [ebp+8]
7C80AC73 E8 AAECFFFF call 7C809922
7C80AC78 3945 0C cmp dword ptr [ebp+C], eax
7C80AC7B 0F84 12600300 je 7C840C93
7C80AC81 8B45 0C mov eax, dword ptr [ebp+C]
7C80AC84 5F pop edi
7C80AC85 5B pop ebx
7C80AC86 C9 leave
7C80AC87 C2 0800 retn 8