使用ASM编写没有导入表的EXE
本帖最后由 Tesla.Angela 于 2010-8-5 22:55 编辑.386
.model flat, stdcall
option casemap :none
.data
DllName db 'user32.dll',0
FunName db 'MessageBoxA',0
.CODE
START:
NoImport proc
local hLib:DWORD
local pMsg:DWORD
;hLib=LoadLibraryA("user32.dll")
push offset DllName
mov eax,7C801D7Bh
call eax
mov hLib,eax
;pMsg=GetProcAddress(hLib,"MessageBoxA")
push offset FunName
push hLib
mov eax,7C80AE40h
call eax
mov pMsg,eax
;MessageBoxA(0,"MessageBoxA","user32.dll",0)
push 0
push offset DllName
push offset FunName
push 0
mov eax,77D507EAh
call eax
;ExitProcess(0)
push 0
mov eax,7C80C0F8h
call eax
NoImport endp
end START
不知道有没有不使用硬编码实现的办法?
这段代码编译出来的EXE大小为1.5KB。
如果注释掉NoImport proc到NoImport endp之间的代码也可以编译,编译出来的EXE大小为1KB,但是运行时出错。 我用来查API地址的小软件 本帖最后由 mxxgt 于 2010-8-6 00:38 编辑
;注意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:
mov eax,DWORD PTR
mov esi,DWORD PTR
lodsd
mov eax,dword ptr
;------------------------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 ;获得PE头偏移地址
add eax,ebx ;计算PE头VA
cmp dword ptr ,00004550h ;验证PE文件的合法性
jne Err ;不合法跳转
mov eax,dword ptr ;导出表地址获得
add eax,ebx ;计算导出表入口VA
push eax ;保存导出表入口VA
mov ecx,eax ;令ecx指向导出表
mov ecx,dword ptr ;找出导出表中函数个数作为循环值
mov eax,dword ptr ;找出函数名字符串地址表的偏移
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 ;获取函数名字符串偏移
add eax,ebx ;计算函数字符串VA
mov edi,eax ;把VA赋值给edi 准备比较
mov esi,dword ptr ;这个地方要算好了!函数内部有3个push,每个push为4个字节一个12个字节
;字符串地址放在call外部倒数第二个push(这个自己用控制好第几个参数是自己定的)
;也就是说从push 字符串地址到这个命令行之间有16个字节被push进入栈中。是不是就是+10H呢
;注意我们这里用了一个函数,不是jmp,函数调用的时候会有一个push eip用于返回!
;所以我们在计算的时候这个push不能忘,最后计算得到esp+14h中所放的就我们要查的函数名字符的地址
mov ecx,dword ptr ; 计算同上,这里是给ecx赋值,函数名的长度。ecx被改变了
;(后面,在loop命令之前要恢复的ecx,这个是计数器,硬性规定,呵呵!)
cld ;
repz cmpsb ;字符串比较 esi 和 edi 比较
jne FindNext ;比较,如果不是,则取下一个函数
add esp,4 ;找到往下执行 相当与pop ecx。无关紧要了!
mov eax,dword ptr ;恢复导出函数表入口
mov eax,dword ptr ;获得函数地址表入口偏移
;我在这里缺少了一步,大多数情况不会有影响,具体的是哪一步,
;深究的同志可以看看关于函数导出表的函数查询方式
add eax,ebx ;计算函数地址表入口VA
shl edx,2 ;函数索引*函数地址(默认为4个字节) 鉴戒别人的,很妙的一招相当与乘以4
add eax,edx ;函数地址表基址+(函数索引)
mov eax,dword ptr ;获得函数入口地址偏移
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
EXE编译大小为1K 汇编.....确实NB,很小很小...... 这个问题不是简单的问题,值得研究!! 名不副实 顶了!!!! 学习了谢谢分享 代码不错必须支持
页:
[1]