あなたは本質的にバイナリ書き換えについて話している。コンパイル プロセスをいじらずにこれを実現する方法の 1 つは、仮想アドレスを物理アドレスにマップしてからパッチを適用することです。興味深いことに、これは私が修士論文で取り上げたものです。次の画像とテキストは、そのドキュメントから抽出されたものです。
私の元のプロジェクトの背後にある概念は、コンパイル プロセスを変更できないと仮定して、他のバイナリのコードを書き直すことでした。要件と仮定が異なる場合、これは最も簡単で最善の方法ではない可能性があります。
ここで最も重要な考え方は、ディスク表現のセクションがメモリにマップされるときに保持される (分割されない) ということです。これは、ディスク表現のセクションへの特定のオフセットにあるデータが、メモリにロードされた後に同じ量だけオフセットされることを意味します。
ではlibelf
、 と同様にlibbfd
、実行可能ファイルには、コードとデータの両方を配置できる一連のセクションが含まれています。オペレーティング システムが実行可能ファイルをメモリにロードするとき、各セクションは何らかのベース アドレスに基づいています。これを逆にして、仮想メモリ アドレスを物理ファイル オフセットにマップできます。物理ファイルのオフセットが見つかった場合は、バイトを通常のファイルとしてパッチできます。
- まず、実行可能ファイルのセクション ヘッダーが で解析され
libelf
ます。これにより、一連のセクションを取得できます。最も重要なことは、各セクションlibelf
が次の 3 つのことを教えてくれることです。
- セクション サイズ セクションのサイズ。
- セクション ベース アドレスディスク上の実行可能ファイルがメモリにロードされるときのセクションのベースとなるアドレス。
- セクション ディスク オフセット セクションのディスク オフセット。
- 前のステップで抽出されたセクション情報を繰り返し処理することにより、任意の仮想メモリ アドレスがどのセクションに含まれているかを見つけることができます。書かれる。セクションへの仮想メモリ アドレスのオフセットは、 で計算できます
(virtual_memory_address - section_base_address)
。
- したがって、仮想メモリ アドレスのディスク オフセットは で計算できます
(section_disk_offset + (virtual_memory_address - section_base_address))
。
このプロセスにより、任意の仮想メモリ アドレスを対応するディスク ファイル オフセットにマップできます。このオフセットは、 // fopen
/などの通常の C ファイル IO 関数でパッチできます。fseek
fwrite
fclose
これは、上記の手順を使用して仮想アドレスを物理ファイル オフセットにマッピングするための私のコードです。
/*
* Returns the corresponding 32 bit executable file offset of a virtual memory
* address.
*/
uint32_t vaddr32_to_file_offset(char * filepath, uint32_t vaddr)
{
int fd = open(filepath, O_RDONLY);
Elf * e = elf_begin(fd, ELF_C_READ, NULL);
uint32_t offset = 0;
Elf_Scn * scn = NULL;
while((scn = elf_nextscn(e, scn)) != NULL) {
Elf32_Shdr * shdr = elf32_getshdr(scn);
if(vaddr >= shdr->sh_addr &&
(vaddr <= (shdr->sh_addr + shdr->sh_size))) {
offset = shdr->sh_offset + (vaddr - shdr->sh_addr);
break;
}
}
elf_end(e);
close(fd);
return offset;
}
/*
* Returns the corresponding 64 bit executable file offset of a virtual memory
* address.
*/
uint64_t vaddr64_to_file_offset(char * filepath, uint64_t vaddr)
{
int fd = open(filepath, O_RDONLY);
Elf * e = elf_begin(fd, ELF_C_READ, NULL);
uint64_t offset = 0;
Elf_Scn * scn = NULL;
while((scn = elf_nextscn(e, scn)) != NULL) {
Elf64_Shdr * shdr = elf64_getshdr(scn);
if(vaddr >= shdr->sh_addr &&
(vaddr <= (shdr->sh_addr + shdr->sh_size))) {
offset = shdr->sh_offset + (vaddr - shdr->sh_addr);
break;
}
}
elf_end(e);
close(fd);
return offset;
}
これは、オフセットを指定して ELF 実行可能ファイルにパッチを適用するコードです。
/*
* Sets the bytes at an arbitrary offset of a file to the contents of buffer.
*/
static bool patch_file(char * filepath, uint64_t offset, void * buffer,
size_t size)
{
FILE * pFile = fopen(filepath, "r+");
if(pFile == NULL) {
return FALSE;
}
fseek(pFile, offset, SEEK_SET);
fwrite(buffer, 1, size, pFile);
fclose(pFile);
return TRUE;
}
より詳細な情報は、ここで公開されているレポート自体に記載されています。