罗云斌老师"动态获取api入口地址"的例子中为什么要"重定位"?

mizihu 2011-10-25 02:00:30
罗云斌老师在讲pe的章节最后有一个"动态获取API入口地址"的例子, 例子中在构建SEH的时候用到了重定位, 代码如下:
_GetKernelBase proc _dwKernelRet
local @dwReturn

pushad
mov @dwReturn,0

;********************************************************************
; 重定位
;********************************************************************
call @F
@@:
pop ebx
sub ebx,offset @B

;********************************************************************
; 创建用于错误处理的 SEH 结构
;********************************************************************
assume fs:nothing
push ebp
lea eax,[ebx + offset _PageError]
push eax
lea eax,[ebx + offset _SEHHandler]
push eax
push fs:[0]
mov fs:[0],esp

;********************************************************************
; 查找 Kernel32.dll 的基地址
;********************************************************************
mov edi,_dwKernelRet
and edi,0ffff0000h
.while TRUE
.if word ptr [edi] == IMAGE_DOS_SIGNATURE
mov esi,edi
add esi,[esi+003ch]
.if word ptr [esi] == IMAGE_NT_SIGNATURE
mov @dwReturn,edi
.break
.endif
.endif
_PageError:
sub edi,010000h
. break .if edi < 070000000h
.endw

pop fs:[0]
add esp,0ch
popad
mov eax,@dwReturn
ret

_GetKernelBase endp

问题1: 为什么在创建用于错误处理的 SEH 结构的时候要用到重定位???
问题2: 如果说这里要用到重定位, 那么为什么代码别的地方不需要用到重定位呢???

我理解的重定位用到的时候是往别人的程序中插入自己写的一段代码的时候, 由于不知道自己的代码将被插入到别人的程序中的什么地方, 因此访问自己代码中的变量的时候会在原来的内存地址中找不到, 因此要利用自己代码中的call来实现在别人的程序中重定位, 可以这里又没有插入到别人的程序中, 为什么还需要重定位??? 如果说这段代码是要插入到别人的程序中的, 那么问题1就有答案了, 但是仍然不能解释问题2, 还请大家多多指教, 小弟我不胜感激.
...全文
212 6 打赏 收藏 转发到动态 举报
写回复
用AI写文章
6 条回复
切换为时间正序
请发表友善的回复…
发表回复
shuirh 2011-10-27
  • 打赏
  • 举报
回复
首先推荐你看一篇文章, 是我从看雪论坛上找到的一本讲病毒的书上节选下来的, 让你彻底弄清楚什么是重定位

1 病毒的重定位技术
病毒的重定位技术应该也算是最基本和最有用的技术之一了,其实重定位技术只要是对外壳开发比较熟悉的朋友都应该很熟悉了。重定位顾名思义重新定位地址。上篇文章中我们已经讲解到了由于我们被感染对象的不同,我们部分定义的变量被插入的地址也不同。所以这就需要我们重新定位,也就是今天的主题——重定位技术。
假如我们定义一段变量
szText db 'Virus Dels Demo', 0
假设我们这个变量在我们病毒体的地址是 00403201h, 那么我们插入到被感染对象后这个变量的地址是 00408602h。那么我们如何能让我们的病毒体代码在运行时期来计算到被插入被感染对象后变量的地址呢? 我们思索下是否能找到一些固定的地方。有时候换位思考是很不错的,我们病毒体既然被插入的地址是不固定的,那么病毒体的一些绝对偏移它相对病毒体的偏移肯定是固定的吧。假设一个病毒的起始位置是 00400021h,那么它插入到被感染对象后的位置是00500021h, 那么我们是不是只要知道一个变量相对于我们病毒起始位置的偏移就可以求得这个变量插入后的位置了?比如我们的szText变量相对于病毒起始位置的偏移是9h, 那么只要通过求得病毒插入到被感染对象后的病毒起始位置 + 这个变量相对病毒起始位置的偏移 = 变量在被感染对
象中的位置。是不是很好理解? 继续思索,我们如何求病毒被插入感染对象后的起始位置,它是可变的。我们来看一下 call指令, call指令是将下一句指令的偏移压入堆栈,然后设置 eip寄存器指向要跳转的地址。看以下代码:
00401010 >/$ E8 00000000 ;call 00401015
00401015 |$ 5B ;pop ebx
就如这段代码 call指令首先将下一句指令的地址 00401015h压入堆栈,然后将 eip寄存器指向 00401015h。病毒技术中 call指令详解: 我们都知道 call 调用一个内存地址的话,编译器编译的是相对于调用地址的偏移(注意:这里不是绝对偏移,相对偏移),求调用地址的公式是调用地址 - (call指令所处偏移 +5) = 相对偏移如上面这段代码, 假设我们的代码是 call 00401016的话, 那么编译器会将机器码编译为 E8 00000001 。 E8为 call对应的机器码。 00000001为偏移差,不懂自己根据上面的公式算算。我想大家看到这点应该明白我讲解 call 指令的作用了吧。。 因为 call是将下句指令偏移压入堆栈,然后设置 eip寄存器为调用地址。所以我们只要在调用地址中取得堆栈中 4字节数据则为下句指令的偏移。 这句代码执行的到 00401015处通过 pop ebx取得堆栈中 4字节的数据(这 4字节数据则为 call 地址所处位置下句指令偏移地址, 也就是 pop ebx指令的地址)。通过这个地址我们就可以把它参照(就相当于上面所说的把它相当于病毒的起始地址),这样我们后面任何绝对地址均采用 这个地址 + 绝对地址相对这个地址的偏移 来取得真正的地址。说起来有些绕口,不过的确是这么个意思。初学者可以多读读理解下。 这里很多不了解基础的人,写代码总是开始 E8 00000000 其实你可以调用其他的地址,然后在其后写一些垃圾代码防止杀软的查杀。然后只要把下句指令偏移作为参照就行了。看代码:
szText db 'Virus Dels Demo', 0
__Entry:
call Dels2
Dels:
int 3
int 3
Dels2:
pop ebx
lea edx, [ebx + szText - Dels] ; edx = szText
ret
代码很简单很好理解,通过调用 call dels, 然后通过 pop ebx获得 dels的偏移,然后通过 szText - Dels获得 szText变量基于 Dels的相对偏移,然后在+dels(ebx) = 重定位后的偏移。如果您能看到这里,我想您已经是基本理解了。其实就是这么简单,找一个能在运行时计算出来的地址,然后+上相对它的偏移则为重定位后的地址。主要思路就是这样,至于如何发挥就是大家的事情了,您可以发挥你的创造力来变形你的重定位代码,任何技术都是基础。

而罗云斌老师在这里用到的重定位, 我个人感觉其实在这里是不需要的, 他在这里这么写, 仅仅是因为后面的代码需要用到, 楼主不要纠结在这个地方, 如果不相信, 可以自己尝试在seh的时候不重定位, 看看是否能够通过, 我自己测试是可以通过的.

如果你需要我的测试源代码或者说是上面讲病毒的书, 可以加我的qq, 或者留下你的email, 我可以传给你.

看得出楼主是想往软件安全工程师这个方向去发展, 我也是且正在努力中, 但是这个东西不是那么好搞的, 希望我的回帖对楼主有帮助.
PctGL 2011-10-27
  • 打赏
  • 举报
回复
这问题主要是在远程注入里面用到比较多

说起来比较麻烦,主要就是为了搜函数地址然后调用, 远程插入一段代码执行, 一般情况下不可避免要用到api,但api函数地址怎么确定? 这时候就用到了那段代码, 通常可以忽略这个问题直接在本进程内取一个api地址,传到远程进程直接调用,一般不会有什么问题,原理问题自己去查... 但如果这个问题放大一点看,修改一个pe文件的代码, 被修改的代码中需要调用 api A,而pe文件中又没有声明api A,就无法确定 A的位置

:( ... 说不清楚了,lz自斟... 等你真正用到了的时候就理解了...
killbug2004 2011-10-26
  • 打赏
  • 举报
回复
_GetKernel.asm这个文件是共用模块,这个函数这样写就是为了加入重定位方便使用,在书的后面有修改可执行文件的例子要求重定位,里面也会用这个模块,这个模块函数不用重定位在"动态获取API入口地址"的例子也是可以的,楼主不必纠结,可以自己试一下,去掉这段里面的重定位编译
mizihu 2011-10-26
  • 打赏
  • 举报
回复
有没有人可以帮我解答一下这个问题啊? 可以继续加分.
火狐狸 2011-10-26
  • 打赏
  • 举报
回复
.386
.Model Flat, StdCall
Option Casemap :None
Include windows.inc
.CODE
Virus:
_GetKernelBase proc _dwKernelRet
local @dwReturn
pushad
mov @dwReturn,0
;********************************************************************
; 重定位
;********************************************************************
call @F
@@:
pop ebx
sub ebx,offset @B
;********************************************************************
; 查找 Kernel32.dll 的基地址
;********************************************************************
mov edi,_dwKernelRet
and edi,0ffff0000h
.while TRUE
.if WORD ptr [edi] == IMAGE_DOS_SIGNATURE
mov esi,edi
add esi,[esi+003ch]
.if WORD ptr [esi] == IMAGE_NT_SIGNATURE
mov @dwReturn,edi
.break
.endif
.endif
sub edi,010000h
.break .if edi < 070000000h
.endw
popad
mov eax,@dwReturn
ret
_GetKernelBase endp
_GetApi proc _hModule,_lpszApi,_cnt
local @dwReturn,@dwStringLength
pushad
mov @dwReturn,0
;********************************************************************
; 重定位
;********************************************************************
call @F
@@:
pop ebx
sub ebx,offset @B
;********************************************************************
; 计算 API 字符串的长度(带尾部的0)
;********************************************************************
mov ecx,_cnt
mov @dwStringLength,ecx
;********************************************************************
; 从 PE 文件头的数据目录获取导出表地址
;********************************************************************
mov esi,_hModule
add esi,[esi + 3ch]
assume esi:ptr IMAGE_NT_HEADERS
mov esi,[esi].OptionalHeader.DataDirectory.VirtualAddress
add esi,_hModule
assume esi:ptr IMAGE_EXPORT_DIRECTORY
;********************************************************************
; 查找符合名称的导出函数名
;********************************************************************
mov ebx,[esi].AddressOfNames
add ebx,_hModule
xor edx,edx
.repeat
push esi
mov edi,[ebx]
add edi,_hModule
mov esi,_lpszApi
mov ecx,@dwStringLength
repz cmpsb
.if ZERO?
pop esi
jmp @F
.endif
pop esi
add ebx,4
inc edx
.until edx >= [esi].NumberOfNames
jmp _Error
@@:
;********************************************************************
; API 名称索引 --> 序号索引 --> 地址索引
;********************************************************************
sub ebx,[esi].AddressOfNames
sub ebx,_hModule
shr ebx,1
add ebx,[esi].AddressOfNameOrdinals
add ebx,_hModule
movzx eax,WORD ptr [ebx]
shl eax,2
add eax,[esi].AddressOfFunctions
add eax,_hModule
;********************************************************************
; 从地址表得到导出函数地址
;********************************************************************
mov eax,[eax]
add eax,_hModule
mov @dwReturn,eax
_Error:
popad
mov eax,@dwReturn
ret
_GetApi endp
hKernel32 DD 0
_GetProcAddress dd 0
nGetProcAddress db 'GetProcAddress',13,10,0
nLoadLibraryA db 'LoadLibraryA',0
_LoadLibraryA dd 0
nKernel db 'kernel32.dll',0
VStart:
call @F
@@:
pop ebx
sub ebx,offset @B
invoke _GetKernelBase,[esp]
mov hKernel32[ebx],eax
lea eax,[offset nGetProcAddress+ebx]
invoke _GetApi,[offset hKernel32+ebx],eax,14
mov _GetProcAddress[ebx],eax
lea eax,[offset nLoadLibraryA+ebx]
invoke _GetApi,[offset hKernel32+ebx],eax,12
mov _LoadLibraryA[ebx],eax
lea eax,[offset nKernel+ebx]
push eax
call _LoadLibraryA[ebx]
mov DWORD ptr hKernel32[ebx],eax
GetOApiz:
call @api_table ; 下面数组的首地址入栈
db 'CreateThread',0
db 'CreateRemoteThread',0
db 'WinExec',0
db 'CreateMutexA',0
db 'OpenMutexA',0
db 'ReleaseMutex',0
db 'FindFirstFileA',0
db 'FindNextFileA',0
db 'FindClose',0
db 'CreateFileA',0
db 'CreateFileMappingA',0
db 'MapViewOfFile',0
db 'UnmapViewOfFile',0
db 'SetFilePointer',0
db 'ReadFile',0
db 'WriteFile',0
db 'CloseHandle',0
db 'VirtualAlloc',0
db 'VirtualAllocEx',0
db 'WriteProcessMemory',0
db 'VirtualFree',0
db 'VirtualFreeEx',0
db 'lstrcmpi',0
db 'lstrcpy',0
db 'lstrcat',0
db 'lstrlen',0
db 'GetFileSize',0
db 'GetSystemDirectoryA',0
db 'GetModuleFileNameA',0
db 'Sleep',0
db 'GetSystemTime',0
db 'DeleteFileA',0
db 'OpenProcess',0
db 'GetModuleHandleA',0
db 'GetCurrentDirectoryA',0
db 'SetCurrentDirectoryA',0
db 'ExitProcess',0
db 'GetExitCodeThread',0
db 'ResumeThread',0
@api_table:
pop edi
call @api_dest ; 原理同上
K_Apiz:
_CreateThread dd 0
_CreateRemoteThread dd 0
_WinExec dd 0
_CreateMutex dd 0
_OpenMutex dd 0
_ReleaseMutex dd 0
_FindFirstFile dd 0
_FindNextFile dd 0
_FindClose dd 0
_CreateFile dd 0
_CreateFileMapping dd 0
_MapViewOfFile dd 0
_UnmapViewOfFile dd 0
_SetFilePointer dd 0
_ReadFile dd 0
_WriteFile dd 0
_CloseHandle dd 0
_VirtualAlloc dd 0
_VirtualAllocEx dd 0
_WriteProcessMemory dd 0
_VirtualFree dd 0
_VirtualFreeEx dd 0
_lstrcmpi dd 0
_lstrcpy dd 0
_lstrcat dd 0
_lstrlen dd 0
_GetFileSize dd 0
_GetSystemDirectory dd 0
_GetModuleFileName dd 0
_Sleep dd 0
_GetSystemTime dd 0
_DeleteFile dd 0
_OpenProcess dd 0
_GetModuleHandle dd 0
_GetCurrentDirectory dd 0
_SetCurrentDirectory dd 0
_ExitProcess dd 0
_GetExitCodeThread dd 0
_ResumeThread dd 0
K_API_NUM=($-K_Apiz)/4
@api_dest:
pop esi
push K_API_NUM
pop ecx
xor ebp,ebp
K_begin:
push ecx ;loop 循环 计数
push edi
push hKernel32[ebx]
call _GetProcAddress[ebx] ; 获取api地址
or eax,eax
jz GA_Fail
mov DWORD ptr [esi],eax ;eax 中的函数地址存入K_Apiz的数组中
GA_Fail:
xor eax,eax
repnz scasb
pop ecx
loop K_begin
call szUser32 ; 下面的首地址入栈
db 'User32.dll',0
szFindWindowA db "FindWindowA",0
szFindWindowExA db "FindWindowExA",0
szSendMessageA db "SendMessageA",0
szChildWindowFromPointEx db "ChildWindowFromPointEx",0
_FindWindowA dd 0
_FindWindowExA dd 0
_SendMessageA dd 0
_ChildWindowFromPointEx dd 0
szUser32:
call _LoadLibraryA[ebx] ;user32.dll
push esi ;??????? 保存esi?
mov esi,eax
call szwsprintfA ;push
db 'wsprintfA',0
_wsprintf dd 0
szwsprintfA:
push esi
call _GetProcAddress[ebx] ;get wsprintf
mov DWORD ptr _wsprintf[ebx],eax
lea ecx,[offset szFindWindowA+ebx]
push ecx
push esi ;user32.dll
call _GetProcAddress[ebx]
mov DWORD ptr _FindWindowA[ebx],eax
lea ecx,[offset szFindWindowExA+ebx]
push ecx
push esi
call _GetProcAddress[ebx]
mov DWORD ptr _FindWindowExA[ebx],eax
lea ecx,[offset szSendMessageA+ebx]
push ecx
push esi
call _GetProcAddress[ebx]
mov DWORD ptr _SendMessageA[ebx],eax
lea ecx,[offset szChildWindowFromPointEx+ebx]
push ecx
push esi
call _GetProcAddress[ebx]
mov DWORD ptr _ChildWindowFromPointEx[ebx],eax
pop esi
ret
VirusLen=$-offset Virus
END VStart
Areslee 2011-10-25
  • 打赏
  • 举报
回复
不是很清楚这东西的上下文,程序本身是可以编译时指定成可以重定位的,哪怕你不是插入别人的代码

21,458

社区成员

发帖
与我相关
我的任务
社区描述
汇编语言(Assembly Language)是任何一种用于电子计算机、微处理器、微控制器或其他可编程器件的低级语言,亦称为符号语言。
社区管理员
  • 汇编语言
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧