专栏名称: 看雪学苑
致力于移动与安全研究的开发者社区,看雪学院(kanxue.com)官方微信公众帐号。
目录
相关文章推荐
波纹的低风险杠杆  ·  20250205 越看越凉的古茗 ·  16 小时前  
波纹的低风险杠杆  ·  20250205 越看越凉的古茗 ·  16 小时前  
东方财富网  ·  突发逆转!暴力拉升超30% ·  2 天前  
东方财富网  ·  突发逆转!暴力拉升超30% ·  2 天前  
人工智能产业链union  ·  “人工智能产业链联盟投融圈”仅限靠谱投资人& ... ·  2 天前  
吾爱破解论坛  ·  【2025春节】解题领红包活动排行榜(初五 ... ·  4 天前  
数据法盟  ·  突发!美国首州封杀DeepSeek及小红书 ·  4 天前  
数据法盟  ·  突发!美国首州封杀DeepSeek及小红书 ·  4 天前  
51好读  ›  专栏  ›  看雪学苑

浅析代码重定位技术

看雪学苑  · 公众号  · 互联网安全  · 2025-02-03 17:58

正文

我将尽量用简单易懂的语言阐述清楚该技术要点,并且辅以相关代码示例帮助新手小白更好的理解。

环境: Windows11 23H2
编译器: ml.exe
链接器: link.exe
适用语言: 32位汇编




什么是代码重定位


代码重定位代码重定位技术是指在程序执行的过程中,操作系统或加载器将程序中的地址引用调整到实际加载时的内存地址的过程, 它允许我们在不同的程序中正确的执行一个片段的代码而不用考虑在当前代码中的实际加载偏移,换句话讲地址无关(Position-Independent Code, PIC)技术的实现就是基于代码重定位来进行实现的。





代码重定位的应用场景


我们考虑如下问题,当前存在一个进程A,他需要在注入进程B之后立马执行一段自己的代码且这段代码中包含地址相关的指令比如某变量的读写。这时我们采用远程线程注入的形式利用APICreateRemoteThreadVirtualAllocExWriteProcessMemory将要执行的汇编代码注入到目标进程中。

首先我们先给出这段代码的汇编实现:

; 2024.11.24 writed by fake77
; kanxue: https://bbs.kanxue.com/homepage-983513.htm
.686
.model flat, stdcall
option casemap:NONE

include windows.inc
include user32.inc
include kernel32.inc

includelib user32.lib
includelib kernel32.lib

WinMain proto :DWORD, :DWORD, :DWORD, :DWORD

.data?
hInstance HINSTANCE ?
CommandLine LPSTR ?

.data
g_szTargetProcessName db "鎵浄", 0
g_szCaption db "Err", 0
g_szErr1 db "Err 1", 0
g_szErr2 db "Err 2", 0
g_szErr3 db "Err 3", 0
g_szErr4 db "Err 4", 0
g_szErr5 db "Err 5", 0
g_szErr6 db "Err 6", 0


.code
INJECT_BEGIN:
; todo inject code
; inject data area
CODE_BEGIN:
; inject asm code area
CODE_END:



WinMain proc hInst:HINSTANCE,hPrevInst:HINSTANCE,CmdLine:LPSTR,CmdShow:DWORD
; alloc local var
LOCAL hWnd:HWND ; handle of window
LOCAL dwPid:DWORD ; process id for open process
LOCAL hProc:DWORD ; process handle
LOCAL pAddr:LPVOID ; target mem pointer
LOCAL dwWrited:DWORD; writed byte num by WriteProcessMemory

; 1. get window handle
invoke FindWindow, NULL, offset g_szTargetProcessName
mov hWnd, eax
.IF !eax
invoke MessageBox, NULL, offset g_szErr1, offset g_szCaption, MB_OK
.ENDIF

; 2. find process
invoke GetWindowThreadProcessId, hWnd, addr dwPid
.IF !eax
invoke MessageBox, NULL, offset g_szErr2, offset g_szCaption, MB_OK
.ENDIF

; 3. get process handle
invoke OpenProcess, PROCESS_ALL_ACCESS, FALSE, dwPid
mov hProc, eax
.IF !eax
invoke MessageBox, NULL, offset g_szErr3, offset g_szCaption, MB_OK
.ENDIF

; 4. alloc mem in target process and prvg set read write
invoke VirtualAllocEx, hProc, NULL, 1000h, MEM_COMMIT, PAGE_EXECUTE_READWRITE
mov pAddr, eax
.IF !eax
invoke MessageBox, NULL, offset g_szErr4, offset g_szCaption, MB_OK
.ENDIF

; 5. write code to target process
invoke WriteProcessMemory, hProc, pAddr, offset INJECT_BEGIN, \
offset CODE_END - offset INJECT_BEGIN, addr dwWrited
.IF !eax
invoke MessageBox, NULL, offset g_szErr5, offset g_szCaption, MB_OK
.ENDIF

; 6. create remote thread to run code
mov eax, pAddr
add eax, offset CODE_BEGIN - offset INJECT_BEGIN ; code entry
invoke CreateRemoteThread, hProc, NULL, 0, eax, NULL, 0, NULL
.IF !eax
invoke MessageBox, NULL, offset g_szErr6, offset g_szCaption, MB_OK
.ENDIF

; makesure stack banlance
ret 10
WinMain endp

start:
invoke GetModuleHandle, NULL
mov hInstance,eax

invoke GetCommandLine
mov CommandLine,eax

invoke WinMain, hInstance,NULL,CommandLine, SW_SHOWDEFAULT
invoke ExitProcess,eax

end start


我们尝试进行注入如下代码,我们希望在目标进程中弹出一个框。


INJECT_BEGIN:
g_szText db "扫雷", 0
g_szCaption db "u got inject", 0
g_pfnMsgBox dd 0
CODE_BEGIN:
invoke MessageBoxA, NULL, offset g_szText, offset g_szCaption, MB_OK
ret 4 ; stdcall 平栈
CODE_END:

我们直接用两个x64dbg进行调试。

这里已经成功将要执行的代码注入到了目标进程中。


我们尝试直接运行看看会发生什么?


可以看到我们这里在执行之后出现了C05这是在我们的意料之中的因为在这里不仅仅是MessageBoxA,整个代码片段都是地址强相关一切的偏移都是在原有进程的偏移所以在新的进程无法使用。

所以在这里我们需要通过地址重定位技术让代码段脱离地址依赖能够自己获取偏移以便于成功运行。

下面我将给出两种重定位技术:

1.段首地址法
2.段偏移法






段首地址法


为什么叫段首地址法这个词很形象的描述了这种方法,当我们在编译汇编程序的时候我们能够确定的并且控制的东西是从地址标签INJECT_BEGINCODE_BEGIN中的各种变量偏移,不能控制的是加载的地址,但是如果我们能够获取到INJECT_BEGIN的地址则可以根据已知的偏移获取到正确的代码偏移量。

到此自身代码的偏移问题已经可以解决,现在我们还需要解决调用系统API的时候在不同的进程中API加载的地址也是不同的问题,那么这个问题如何解决呢?

对于这个问题我们应该利用系统自身带的PIC API去进行定位,看过我写的文章的小伙伴应该已经知道了,没错就是LoadLibraryGetProcAddress这两个API是系统自带的PIC API, 他们通常在不同进程中的地址是相同的,我们通过这个API将MessageBoxA的地址加载到要注入的地址中去。

tips:对于32位汇编我们不能直接拿到IP寄存器的值我们需要通过call命令将返回地址压入栈中然后利用pop手动获取到IP的值,对于地址重定向中我们获取到当前执行的命令的IP值则可以通过如下代码段。

call $ + 5 ; 这里值得注意的是 + 5 是因为call指令的长度是5个字节
; 它的字节码为 E8 00 00 00 00
; 而 $ 符则是在编译器获取当前指令的地址而 + 5是因为
; call 和 jmp一样都是获取的下一条指令的IP当我们call
; $ + 5的时候就可以直接获取到当前 pop eax这条指令的地址
pop eax

基于如上原理我们给出如下实现:

.code
INJECT_BEGIN:
g_szText db "扫雷", 0
g_szRemoteCaption db "u got inject", 0
g_pfnMsgBox dd 0 ; MessageBoxA指针
CODE_BEGIN:
call $+5
pop ebx ;获取当前指令地址
sub ebx, (offset CODE_BEGIN - offset INJECT_BEGIN) + 5 ; 获取到段首的偏移

push MB_OK

mov eax, offset g_szText - offset INJECT_BEGIN ; 获取g_szText到段首的偏移
add eax, ebx
push eax

mov eax, offset g_szRemoteCaption - offset INJECT_BEGIN ; 获取g_szRemoteCaption到段首的偏移
add eax, ebx
push ebx

push NULL

mov eax, offset g_pfnMsgBox - offset INJECT_BEGIN
add eax, ebx
call dword ptr [eax] ; 通过指针调用

; invoke MessageBoxA, NULL, offset g_szText, offset g_szRemoteCaption, MB_OK

ret 4 ; stdcall 平栈CODE_END:

CODE_END:

MessageBox地址获取实现:
  

; WinMain....
; 因为是代码段所以要更改下权限
invoke VirtualProtect,offset INJECT_BEGIN, 1000h, PAGE_EXECUTE_READWRITE, addr dwOld
; check ...
; 获取user32的地址
invoke LoadLibrary, offset g_user32
; check ...
mov hUser32, eax
; 获取到MessageBoxA的地址
invoke GetProcAddress, hUser32, g_message
; check ...
mov g_pfnMsgBox, eax
; 还原权限
invoke VirtualProtect,offset INJECT_BEGIN, 1000h, dwOld, addr dwOld
; check ...

; WinMain....

我们通过调试来观察下过程:


成功定位到代码段:


成功运行!至此就是段首地址法的内容。。


下面我们进入段偏移法的讲解。





段偏移法


段偏移法段首地址法的最大区别就是段偏移法是通过计算加载到目标进程的地址和原进程地址的偏移,通过原进程地址 + 偏移 = 目标进程的地址的方式重定位代码段。

给出如下实现:

.code
INJECT_BEGIN:
g_szText db "扫雷", 0
g_szRemoteCaption db "u got inject", 0
g_pfnMsgBox dd 0 ; MessageBoxA指针
CODE_BEGIN:
call $+5
NEXT:
pop ebx ;获取当前指令地址
; sub ebx, (offset CODE_BEGIN - offset INJECT_BEGIN) + 5 ; 获取到段首的偏移

sub ebx, offset NEXT ; NEXT是在编译其确定的地址而ebx则是运行时确定的其差值就是偏移值.

push MB_OK

;mov eax, offset g_szRemoteCaption - offset INJECT_BEGIN ; 获取g_szRemoteCaption到段首的偏移
mov eax, offset g_szText
add eax, ebx
push eax


;mov eax, offset g_szText - offset INJECT_BEGIN ; 获取g_szText到段首的偏移
mov eax, offset g_szRemoteCaption
add eax, ebx
push eax

push NULL

; mov eax, offset g_pfnMsgBox - offset INJECT_BEGIN
mov eax, offset g_pfnMsgBox
add eax, ebx
call dword ptr [eax] ; 通过指针调用

; invoke MessageBoxA, NULL, offset g_szText, offset g_szRemoteCaption, MB_OK

ret 4 ; stdcall 平栈CODE_END:

CODE_END:

成功重定位到代码段:


成功运行:


代码重定位技术在许多计算机科学和软件工程领域中扮演着重要角色,尤其是在注入技术、动态链接库(DLL)、以及操作系统内存管理等方面。掌握代码重定位技术不仅有助于理解这些领域的基础概念,还能为开发和安全研究提供强大的支持。





看雪ID:TeddyBe4r

https://bbs.kanxue.com/user-home-983513.htm

*本文为看雪论坛优秀文章,由 TeddyBe4r 原创,转载请注明来自看雪社区








请到「今天看啥」查看全文

推荐文章
波纹的低风险杠杆  ·  20250205 越看越凉的古茗
16 小时前
波纹的低风险杠杆  ·  20250205 越看越凉的古茗
16 小时前
东方财富网  ·  突发逆转!暴力拉升超30%
2 天前
东方财富网  ·  突发逆转!暴力拉升超30%
2 天前
知心  ·  你快停下,我老公快醒了
7 年前
北京小升初网  ·  致小升初上岸家长们的一封信
7 年前
交互设计学堂  ·  界面交互动效核心知识的分类与总结
7 年前