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=>行番号、関数名は出せるだろう。

で、バックトレース出してできあがりだな。