给我修改个初始化参数吧
有天公司硬件找到我,要我给他修改一个功放初始化参数,调试效果。
找到功放驱动源码,功放初始化参数是写在一个二维数组,地址和数据一一对应,在系统初始化过程,一个个写进功放。
在他看来需求很简单,改一个寄存器的初始化值然后编译输出一个临时软件给他验证。
可是,这套代码很庞大,编译非常耗时,而且还要考虑版本匹配等问题,好吧,得动点歪主意了。
认识 nm 命令
只是改个数字,直接二进制修改即可吧,拿到硬件机器的可执行文件直接修改完再放回去就好嘛。
但这需要知道这部分数据放在可执行文件的什么地方,想起 nm 命令和可以查看可执行文件的符号信息。
回顾一下 nm 命令:
nm 命令可以读出可执行对象的符号表及符号类型、符号的值。
常用的参数如下
- -a :显示所有符号;
- -C : 使 C++ 函数名更具可读性。
符号类型说明:
对于每个符号来说,如果符号类型是小写的,则表示该符号是 local 的;大写则表明该符号是 globle(external)的。
- A :表示该符号是绝对的,不允许改变,如中断向量表;
- B :表示该符号出现在非初始化数据段(bss)中;
- D :表示该符号位于初始化数据段中;
- R :表示该符号位于只读数据区;
- U :表示该符号在当前文件中是未定义的,即改符号出现在别的文件中。
其中关于符号值的说法,在程序运行时应当是指的是在内存中地址,而这个符号值的地址和它在文件中的地址是否对应的上呢,我想它应该和程序加载地址有关,这里我没去深究它,这个值和它在文件中的地址还是对的上的。
一个例子
上码:
#include <stdio.h>
#include <stdint.h>
char data_1[] = "hello";
static char data_2[] = "world";
const static char data_3[] = "!!!";
int main(int argc, char *argv[])
{
printf("%s %s%s\r\n", data_1, data_2, data_3);
printf("0x%x 0x%x 0x%x\r\n", data_1, &data_2, &data_3);
printf("%s\r\n", (char*)0x601038);
return 0x00;
}
其中第一个 printf 打印他们的值,等下我们会使用二进制的方式来修改它;
第二个 printf 打印的是他们的地址;
第三个 printf 是打印 0x601038 地址的值,这里我提前用 nm 知道这个地址是 data_1 的。
使用 gcc 编译输出二进制 main,然后执行 nm main 后输出如下(只截取了部分):
0000000000601044 B __bss_start
0000000000601044 b completed.7594
0000000000601038 D data_1
000000000060103e d data_2
0000000000400614 r data_3
0000000000601028 D __data_start
0000000000601028 W data_start
............................
0000000000400526 T main
U printf@@GLIBC_2.2.5
00000000004004a0 t register_tm_clones
0000000000400430 T _start
执行 main 输出如下:
hello world!!!
0x601038 0x60103e 0x400614
hello
修改试试看
让我们试试修改 !!! 为 ???,首先我们得有个好的 bin 档编辑器,这里推荐使用 HxD Hex Editor,打开后我们试试 0x400614 这个地址,发现找不到,让我们再试试 0x614 这个地址,发现找到了 !!! 如下图:
这里前面的 60 和 40 究竟是怎么来的,我还没查到,让我们先来把它修改成 ??? 吧,如下图:
保存一下,然后再执行 main 发现输出变成了如下:
hello world???
0x601038 0x60103e 0x400614
hello
是不是很有趣 _
扫码关注我了解更多,元旦假期愉快! _