PE と stab
TCCはstabらしい。stabについてはGDBに入ってるstabs.infoが詳しい。
とりあえず.stabセクションまで到達しよう。
#include <windows.h> int main(int argc, char **argv) { HANDLE h, map; unsigned char *mem; PIMAGE_DOS_HEADER dos; PIMAGE_FILE_HEADER pfh; WORD u2; DWORD u4; int num_sec, i; PIMAGE_SECTION_HEADER sections; if (argc < 2) { return 1; } h = CreateFile(argv[1], GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (h == INVALID_HANDLE_VALUE) { puts("e-"); return 1; } map = CreateFileMapping(h, NULL, PAGE_READONLY, 0, 0, NULL); mem = (unsigned char*)MapViewOfFile(map, FILE_MAP_READ, 0, 0, 0); dos = (PIMAGE_DOS_HEADER)mem; u2 = dos->e_magic; if (u2 != IMAGE_DOS_SIGNATURE) { puts("not mz"); return 1; } u4 = *(DWORD*)(mem + dos->e_lfanew); if (u4 != IMAGE_NT_SIGNATURE) { puts("not nt"); return 1; } pfh = (PIMAGE_FILE_HEADER)(mem + dos->e_lfanew + 4); num_sec = pfh->NumberOfSections; sections = (PIMAGE_SECTION_HEADER)(mem + dos->e_lfanew + 4 + sizeof(IMAGE_FILE_HEADER) + sizeof(IMAGE_OPTIONAL_HEADER)); for (i=0; i<num_sec; i++) { printf("%d:%s, char=%x, ptrto=%x\n", i,sections[i].Name, sections[i].Characteristics, sections[i].PointerToRawData); } }
TCCの出すPEならこれで到達できる(他は知らん)
というか tcc.c の 9723行目あたりに既にあるように見えるな…まあいいか。見なかったことにしよう。(見るが)
(ここで色々あった)
まあ大体とれた。
#include <windows.h> struct stab_nlist { unsigned long n_strx; unsigned char n_type; unsigned char n_other; unsigned short n_desc; unsigned long n_value; }; int main(int argc, char **argv) { HANDLE h, map; unsigned char *mem; PIMAGE_DOS_HEADER dos; PIMAGE_FILE_HEADER pfh; WORD u2; DWORD u4; int num_sec, i; PIMAGE_SECTION_HEADER sections; struct stab_nlist *stabs = NULL; unsigned char *stabstr = NULL; int num_stab; int stab_size; if (argc < 2) { return 1; } h = CreateFile(argv[1], GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (h == INVALID_HANDLE_VALUE) { puts("e-"); return 1; } map = CreateFileMapping(h, NULL, PAGE_READONLY, 0, 0, NULL); mem = (unsigned char*)MapViewOfFile(map, FILE_MAP_READ, 0, 0, 0); dos = (PIMAGE_DOS_HEADER)mem; u2 = dos->e_magic; if (u2 != IMAGE_DOS_SIGNATURE) { puts("not mz"); return 1; } u4 = *(DWORD*)(mem + dos->e_lfanew); if (u4 != IMAGE_NT_SIGNATURE) { puts("not nt"); return 1; } pfh = (PIMAGE_FILE_HEADER)(mem + dos->e_lfanew + 4); num_sec = pfh->NumberOfSections; sections = (PIMAGE_SECTION_HEADER)(mem + dos->e_lfanew + 4 + sizeof(IMAGE_FILE_HEADER) + sizeof(IMAGE_OPTIONAL_HEADER)); for (i=0; i<num_sec; i++) { BYTE *name = sections[i].Name; printf("%d:%s, char=%x, size=%x, ptrto=%x\n", i,sections[i].Name, sections[i].Characteristics, sections[i].Misc.VirtualSize, sections[i].PointerToRawData); if (memcmp(name, ".stab", 6) == 0) { stabs = (struct stab_nlist*)(mem + sections[i].PointerToRawData); stab_size = sections[i].Misc.VirtualSize/sizeof(struct stab_nlist); } else if (memcmp(name, ".stabstr", 8) == 0) { stabstr = (unsigned char*)(mem + sections[i].PointerToRawData); } } if (stabs == NULL || stabstr == NULL) { puts("Not found stab section"); return 1; } printf("numstab = %x\n", stab_size); for (i=0; i<stab_size; i++) { printf("%d:%x %x %x %x %x\n", i, stabs[i].n_strx, stabs[i].n_type, stabs[i].n_other, stabs[i].n_desc, stabs[i].n_value); } }
TCCの吐いてるstabは以下のよう(他のコンパイラが同じかどうかは知らない)。
型情報は調べてないのでわからない。
- 関数の先頭にN_FUN(0x24)が付く。n_valueに関数アドレス、n_descに行番号
- 適当にN_SLINE(0x44)。n_valueに関数先頭からのアドレス、n_descに行番号
- 関数の終わりにN_FUN(0x24)。n_strxとn_descが0。n_valueに関数サイズ
これでEIP=>行番号、関数名は出せるだろう。
で、バックトレース出してできあがりだな。