title: 全局變量反匯編與重定位
date: 2019/02/23 21:10:08
toc: true
全局變量反匯編與重定位
引入
本文完整代碼地址 https://gitee.com/layty/Jz2440/tree/master/Assembler/02-global_test
思考這樣一個問題,全局變量是怎么存儲的,怎么使用的,反匯編后是什么樣子的?
-
最理想的取地址應該是這樣的,但是並沒有這樣的指令,操作數不可能是一個32位的數
ldr r0,[a的地址]
-
所以我們還想這樣,還需要 將 r1 賦值32位的地址..所以單純的賦值沒有辦法實現這個操作
ldr r0,[r1]
-
也就是需要這么做
ldr r0,[pc,offset] ;lable1 lable1: addr_val label2_addr_val: val
我們寫這樣一段代碼測試一下.
#include <stdio.h>
int a=0;
int b=1;
int test(int c)
{
c=c+1;
printf("hello %d",c);
return c;
}
typedef int (*funpt)(int);
funpt pt =&test;
int main()
{
a=3;
b=b+1;
test(a);
pt(b);
}
makefiel如下,反匯編中加入c語言信息,加入-S
選項,cc
中加入-g
選項
all:
arm-linux-gcc -g -o o.out globl_var.c -Wl,-Map=gcc.map
arm-linux-objdump -D -S o.out>o.dis
claen:
rm *.out
nopie分析
一般我們編譯的時候都是nopie,pie我是在編譯uboot時候了解到的,本篇文章也是關於uboot重定位分析的子章節
MAP
我們先來看下map文件的地址,有助於后續的分析
.text 0x00008380 test
.bss 0x000105f8 a
.data 0x000105f0 pt
0x000105ec b
普通變量
從main
函數入手,先從[pc, #88]
的地方取得一個值,再從這個值所表示的地址中取值
a=3;
83cc: e59f2050 ldr r2, [pc, #80] ; 8424 <main+0x64>
83d0: e3a03003 mov r3, #3 ; 0x3
83d4: e5823000 str r3, [r2]
8424: 000105f8 .word 0x000105f8
8428: 000105ec .word 0x000105ec
842c: 000105f0 .word 0x000105f0
000105ec <b>:
105ec: 00000001 .word 0x00000001
000105f0 <pt>:
105f0: 00008380 .word 0x00008380
Disassembly of section .bss:
000105f4 <completed.5877>:
105f4: 00000000 .word 0x00000000
000105f8 <a>:
105f8: 00000000 .word 0x00000000
從代碼上可以看出來8424
這個地方存儲的就是變量的地址,也就是這么一個示意圖
可以看到a所表示的地址的值,確實是0,實際上這個是存在bss段的.
接着來看代碼b=b+1
,多了一個取值的步驟
b=b+1;
83d8: e59f3048 ldr r3, [pc, #72] ; 8428 <main+0x68>
83dc: e5933000 ldr r3, [r3]
83e0: e2832001 add r2, r3, #1 ; 0x1
83e4: e59f303c ldr r3, [pc, #60] ; 8428 <main+0x68>
83e8: e5832000 str r2, [r3]
8424: 000105f8 .word 0x000105f8
8428: 000105ec .word 0x000105ec
842c: 000105f0 .word 0x000105f0
000105ec <b>:
105ec: 00000001 .word 0x00000001
指針變量
接着來看下函數指針pt
的處理
pt(b);
; 取出指針的地址,這個和我們普通變量操作中先取出變量的地址是一樣的
83fc: e59f3028 ldr r3, [pc, #40] ; 842c <main+0x6c>
; 獲取變量的值,也就是指針的值,這里就是test的地址值了
; 可以看到 [000105f0]=00008380 確實是 00008380 <test>:
8400: e5932000 ldr r2, [r3]
;取出變量b的地址 然后取出b的值
8404: e59f301c ldr r3, [pc, #28] ; 8428 <main+0x68>
8408: e5933000 ldr r3, [r3]
; 傳遞為第一個參數
840c: e1a00003 mov r0, r3
8410: e1a0e00f mov lr, pc
;r2在上面賦值就是test的值
8414: e12fff12 bx r2
842c: 000105f0 .word 0x000105f0
000105f0 <pt>:
105f0: 00008380 .word 0x00008380
00008380 <test>:
print函數
在函數中調用了庫函數,這里也是使用了相對跳轉了
printf("hello %d",c);
839c: e59f0018 ldr r0, [pc, #24] ; 83bc <test+0x3c>
83a0: e51b1008 ldr r1, [fp, #-8]
83a4: ebffffc5 bl 82c0 <_init+0x48>
pie分析
接下去的分析都是針對重定位uboot的,與一般的程序沒有關系了,不屬於全局變量反匯編的分析
MAP
.text 0x00008380 test
.bss 0x000105f8 a
.data 0x000105f0 pt
0x000105ec b
// 上面是nopie 下面是pie的
.text 0x000005f8 test
.data 0x0000887c pt
.data 0x00008878 b
.bss 0x00008884 a
匯編方式
加入pie選項和其實發現,單純的這段main
和test
代碼是一致的
地址分析
69c: 00008884 .word 0x00008884 ;存放的a的地址的lable
6a0: 00008878 .word 0x00008878 ;存放的b的地址的lable
6a4: 0000887c .word 0x0000887c ;存放的pt的地址的lable
搜索這幾個地址值69c
,6a0
,6a4
00000400 <.rel.dyn>:
;a
420: 0000069c muleq r0, ip, r6
424: 00000017 andeq r0, r0, r7, lsl r0
;b
428: 000006a0 andeq r0, r0, r0, lsr #13
42c: 00000017 andeq r0, r0, r7, lsl r0
;pt
430: 000006a4 andeq r0, r0, r4, lsr #13
434: 00000017 andeq r0, r0, r7, lsl r0
450: 0000887c andeq r8, r0, ip, ror r8
454: 00000017 andeq r0, r0, r7, lsl r0
同時,還有一個關鍵的東西,就是指針的值是個地址的時候,他的值也會出現在這個表里面
450: 0000887c andeq r8, r0, ip, ror r8
454: 00000017 andeq r0, r0, r7, lsl r0
這是什么意思呢
當指針的值是個地址的時候,當重定位之后,這個地址應該同樣加上偏移.
uboot的pie
.bss 0x000ae4e0 0x4 board/samsung/smdk2410/libsmdk2410.o
0x000ae4e0 a
.text 0x000560dc 0x240 board/samsung/smdk2410/libsmdk2410.o
0x00056278 main2
0x00056258 test
.data 0x0006adfc 0x8 board/samsung/smdk2410/libsmdk2410.o
0x0006adfc b
0x0006ae00 pt
變量分析
00056278 <main2>:
56278: e92d4010 push {r4, lr}
;取出b的地址
5627c: e59f402c ldr r4, [pc, #44] ; 562b0 <main2+0x38>
;r4=0006adfc是b的地址
;取出a的地址
56280: e59f302c ldr r3, [pc, #44] ; 562b4 <main2+0x3c>
56284: e5942000 ldr r2, [r4]
56288: e3a01003 mov r1, #3 ; 0x3
5628c: e2822001 add r2, r2, #1 ; 0x1
56290: e1a00001 mov r0, r1
56294: e5831000 str r1, [r3]
56298: e5842000 str r2, [r4]
5629c: ebffffed bl 56258 <test>
; pt(b)
562a0: e5940000 ldr r0, [r4]
562a4: e1a0e00f mov lr, pc
;到這里為止,r4依然是b的地址,也就是說 pt的地址就是b后面,pt的尋址都是按照b來的
;手動計算一下 r4+4=6ae00 這確實是pt的實際地址
; 0006ae00 <pt>:
; 6ae00: 00056258 .word 0x00056258
562a8: e594f004 ldr pc, [r4, #4]
562ac: e8bd8010 pop {r4, pc}
我們來看下具體的地址信息
;----------------------------------------------------------------------
562b0: 0006adfc .word 0x0006adfc ;這個存的是b的地址
562b4: 000ae4e0 .word 0x000ae4e0 ;這個存的是a的地址
; 這里沒有lable存儲着pt的地址,程序依靠先找到b的地址,b地址下一個偏移就是pt的地址
; pt里面的值也是個地址
0006ae00 <pt>:
6ae00: 00056258 .word 0x00056258
接下來的就是應該重定位的變量信息,也就是lable和地址,可以看到lable和指針都出現了這張表里面
;存放b的lable的地址
71854: 000562b0 .word 0x000562b0
71858: 00000017 .word 0x00000017
;存放a的lable的地址
7185c: 000562b4 .word 0x000562b4
71860: 00000017 .word 0x00000017
;這里沒有存放pt的lable地址 ,依靠b的地址來尋找
;pt的值也是個地址值,那么這個地址也應該處理
72c1c: 0006ae00 .word 0x0006ae00
72c20: 00000017 .word 0x00000017
小結重定位數據段
也就是說,這個特殊的段<.rel.dyn>
應該存放有所有值是地址的單元的地址.
反匯編文件可以看成數據段加指令段,這里考慮數據段
0000: ....
....
xxxx: val 當val表示的是一個地址的時候,現在有兩種情況
也就是說包含了
- lable 的地址,存放的是全局變量的地址
- 指針的地址,這個指針存放的是個地址
有了上面的<.rel.dyn>
段重定位數據段就很方便了,將里面的地址挑出來,對其值和其存放的地址都加上偏移即可
- 取出a的地址,此時a的地址應該加上offset
- a的地址怎么取的,也是從一個lable中取的,這個lable也是經過offset的了
- 當a的值是個地址,比如是個函數指針,這個函數指針的值也應該加上offset,才是新定位的函數地址
總結起來一句話