📄 newcoff.cpp
字号:
// newcoff.cpp
// Copyright (C) 2009 Willow Schlanger
#include "newcoff.h"
#include "newpdb.h"
#include <iostream>
#include "util.h"
namespace ceres
{
// This caches DLL accesses.
struct coff_state_t
{
std::string root; // e.g. MYFILE.EXT
loaded_target_t *primary; // target for the root
std::map<std::string, loaded_target_t> secondary; // other targets. key is e.g. MYDLL.DLL
// given a filename (e.g. X.DLL), this returns a pointer to the loaded target in
// question or NULL if it has not yet been loaded.
loaded_target_t *get_target(std::string filename)
{
filename = make_uppercase(filename);
if(filename == root)
return primary;
std::map<std::string, loaded_target_t>::iterator i = secondary.find(filename);
if(i != secondary.end())
return &i->second;
return NULL;
}
};
//--- begin coff
struct PACKED(dos_header_t)
{
U1 signature[2]; // {'M','Z'} or {'Z','M'}
U2 size_mod_512;
U2 size_div_512; // includes any partial last page
U2 num_relocs;
U2 header_size; // in paragraphs (1 paragraph = 16 bytes)
U2 min_mem; // in paragraphs
U2 max_mem; // in paragraphs
U2 initial_ss; // relative to start of executable
U2 initial_sp;
U2 checksum;
U2 initial_ip;
U2 initial_cs; // relative to start of executable
U2 reloc_offset; // within header. >= 0x40 for new executables.
U2 overlay_num; // normally 0 = main program
// New executables:
U4 unknown; // ???
U2 behavior; // ?
U1 behavior_reserved[26]; // reserved for additional behavior info
U4 new_header_offset; // within disk file
};
enum
{
it_pe32,
it_coff32
};
struct loaded_image_t
{
U4 image_type; // it_*
U1 *file_image; // NULL
U4 file_image_size; // zero
U1 *image;
U4 image_size;
};
struct PACKED(coff_data_dir_t)
{
U4 rva; // rva = address of the table, when loaded, relative to the base of the image
U4 size; // size in bytes
};
struct PACKED(coff_t)
{
U2 machine; // 0x14c = i386+
U2 number_of_sections;
U4 time_date_stamp;
U4 pointer_to_symbol_table; // file offset
U4 number_of_symbols;
U2 size_of_optional_header;
U2 characteristics;
U2 magic; // 0x10b (PE32) or 0x20b (PE32+)
U1 linker_version_major;
U1 linker_version_minor;
U4 code_size; // total size of all text sections
U4 initialized_data_size; // total size of all data sections
U4 uninitialized_data_size; // total size of all bss sections
U4 entrypoint_address; // rel. to image base when executable is loaded into memory
U4 base_of_code; // rel. to image base when loaded into memory; beginning of code section
U4 base_of_data; // (absent in PE32+); beginning of data section
U4 image_base; // must be a multiple of 64kb. preferred address of 1st byte of image when loaded into memory.
U4 section_alignment; // >= file_alignment. typically 4096. sections are aligned to this when loaded into memory.
U4 file_alignment; // sections in the file are aligned to this many bytes. typically 512.
U2 major_os_version;
U2 minor_os_version;
U2 major_image_version;
U2 minor_image_version;
U2 major_subsystem_version;
U2 minor_subsystem_version;
U4 reserved1; // win32_version_value
U4 size_of_image; // size of image in bytes, including all headers. a multiple of section_alignment.
U4 size_of_headers; // DOS stub, PE header, and section headers rounded up to a multiple of file alignment
U4 check_sum;
U2 subsystem; // 2=gui, 3=console
U2 dll_characteristics;
U4 stack_reserve;
U4 stack_commit;
U4 heap_reserve;
U4 heap_commit;
U4 loader_flags; // obsolete
U4 number_data_dirs; // typically 16
coff_data_dir_t data_dirs[1]; // variable size
};
/*
data_dirs[0] = export table
data_dirs[1] = import table
data_dirs[2] = resource table
data_dirs[3] = exception table
data_dirs[4] = certificate table. RVA is really a file pointer.
data_dirs[5] = base relocation table
data_dirs[6] = debug data
data_dirs[7] = architecture-specific data
data_dirs[8] = size is 0. RVA of value to be stored in global pointer register.
data_dirs[9] = thread-local storage (TLS) table
data_dirs[10] = load configuration table
data_dirs[11] = bound import table
data_dirs[12] = import address table (IAT)
data_dirs[13] = delay import descriptor
data_dirs[14] = COM+ runtime header
data_dirs[15] = reserved
*/
struct PACKED(coff_section_header_t)
{
char name[8]; // 8-byte, null-padded, ascii string. no terminating null if length is exactly 8 bytes.
U4 virtual_size; // (or physical address). total size of section when loaded in memory. if > size of raw data, is 0 padded.
U4 virtual_address; // address of 1st byte of section, when loaded in memory, relative to image base.
// Note. raw_data_size is the size on disk. it's rounded up to a multiple of file alignment.
// So, if the actual data size is 513 bytes but file alignment is 512, this will be 1024. But virtual_size will still be 513!
U4 raw_data_size; // size of initialized data on disk. must be a multiple of file alignment.
U4 raw_data_pointer; // file pointer to raw data. 0 if section contains only uninitialized data.
U4 relocation_pointer; // zero
U4 linenum_pointer; // file pointer to line number entries for section, or zero if none.
U2 num_relocations; // zero
U2 num_linenum_entries;
// 0x00000020 - contains executable code
// 0x00000040 - contains initialized data
// 0x00000080 - contains uninitialized data
// 0x20000000 - executable
// 0x40000000 - readable
// 0x80000000 - writable
U4 characteristics;
};
// The IAT is an array of revc_iat's, terminated by a null one.
// This is really an import directory table, not an import address table. :)
struct PACKED(iat_t)
{
// import_lookup_table: mask by 0x7fffffff. If bit 31 was 1,
// import by ordinal. The ordinal is the value &'d by 0x7fffffff.
// else, import by hint/name. rva of image hint/name, with this
// format:
// U2 hint;
// char asciiz_name[<variable size>].
// [another null is appended to pad to an even address]
U4 import_lookup_table_rva; // U4's, terminated by 0.
U4 time_date_stamp;
U4 forwarder_chain;
U4 name_rva; // asciiz e.g. "kernel32.dll"
U4 iat_rva; // thunk table (these are overwritten)
};
struct PACKED(edt_t)
{
U4 characteristics;
U4 time_date_stamp;
U2 major_version;
U2 minor_version;
U4 real_name_rva;
U4 ordinals_base; // e.g. 1, if ordinal 1 is at index 0
U4 number_of_functions;
U4 number_of_names;
U4 rva_functions;
U4 rva_names;
U4 rva_ordinals;
};
// if a loaded_image_t struct is of type it_pe32, use this function
// on its 'file_image' or 'image' element to get the offset where
// the COFF header starts. Returns 0 on error.
U4 get_coff32(U1 *image, U4 image_size)
{
if(image_size < 0x1c)
return 0;
dos_header_t *dos = (dos_header_t *)image;
if(dos->signature[0] != 'M' || dos->signature[1] != 'Z')
if(dos->signature[0] != 'Z' || dos->signature[1] != 'M')
return 0;
U4 exe_size = dos->size_div_512 * 512 + dos->size_mod_512;
if(dos->size_mod_512 != 0)
exe_size -= 512;
U4 hdr_size = dos->header_size * 16;
if(hdr_size > image_size || hdr_size > exe_size)
return 0;
if(hdr_size < 0x40)
return 0;
U4 new_offset = dos->new_header_offset;
if(new_offset + 4 > image_size)
return 0;
if(image[new_offset] != 'P' || image[new_offset+1] != 'E' ||
image[new_offset+2] != '\0' || image[new_offset+3] != '\0'
)
return 0;
return new_offset + 4;
}
bool load_image(loaded_image_t &image)
{
U4 coff_offset = 0;
if(image.image_type == it_coff32)
;
else
if(image.image_type == it_pe32)
{
coff_offset = get_coff32(image.file_image, image.file_image_size);
if(coff_offset == 0)
return false;
}
U4 coff_size = image.file_image_size - coff_offset;
if(coff_size < sizeof(coff_t) - sizeof(coff_data_dir_t))
return false;
coff_t *coff = (coff_t *)(image.file_image + coff_offset);
if(coff->machine != 0x14c || coff->magic != 0x10b)
return false;
// is valid - now do the load!
image.image_size = coff->size_of_image;
image.image = new U1 [image.image_size];
for(U4 i = 0; i < image.image_size; ++i)
image.image[i] = 0;
// Copy header - including DOS stub
for(U4 i = 0; i < coff->size_of_headers; ++i)
image.image[i] = image.file_image[i];
coff_section_header_t *sections = (coff_section_header_t *)(image.file_image +
coff_offset + sizeof(coff_t) - sizeof(coff_data_dir_t) +
sizeof(coff_data_dir_t) * coff->number_data_dirs
);
for(U4 i = 0; i < coff->number_of_sections; ++i)
{
if(sections[i].raw_data_pointer == 0)
continue;
U4 copy_size = (sections[i].raw_data_size < sections[i].virtual_size) ?
copy_size = sections[i].raw_data_size :
copy_size = sections[i].virtual_size
;
U1 *src = image.file_image + sections[i].raw_data_pointer;
U1 *dest = image.image + sections[i].virtual_address;
for(U4 x = 0; x < copy_size; ++x)
dest[x] = src[x];
}
return true;
}
//--- end coff
U1 *do_load_coff(std::string path, std::string file, U8 &size)
{
U8 raw_size;
int status = 0;
size = 0;
U1 *raw = load_file((path + file).c_str(), size, status);
if(status != 0)
{
raw = load_file(file.c_str(), size, status);
if(status != 0)
{
return NULL;
}
}
loaded_image_t image;
image.file_image = raw;
image.image_type = it_coff32;
image.file_image_size = size;
bool ok;
ok = load_image(image);
if(!ok)
{
image.image_type = it_pe32;
ok = load_image(image);
if(!ok)
{
delete [] raw;
return NULL;
}
}
delete [] raw;
image.file_image = NULL;
size = (U8)(U4)(image.image_size);
return image.image;
}
U1 *load_target(std::string path, std::string file, loaded_target_t &target)
{
U8 size = 0;
target.clear();
U1 *bin = do_load_coff(path, file, size);
if(bin == NULL)
{
return NULL;
}
coff_t *coff = (coff_t *)(bin + get_coff32(bin, size));
if(coff == NULL)
{
delete [] bin;
return NULL;
}
target.size = size;
target.base = coff->image_base;
target.start = coff->entrypoint_address;
target.module = remove_extension(make_uppercase(file));
return bin;
}
std::string resolve_symbol(dll_finder_t &finder, std::string dll_name, int ordinal, coff_state_t &s);
std::string resolve_symbol(dll_finder_t &finder, std::string dll_name, std::string procedure, coff_state_t &s);
std::string do_resolve_symbol(dll_finder_t &finder, std::string dll_name, std::string procedure, coff_state_t &s)
{
if(dll_name.empty())
return "";
if(dll_name[0] == '#')
{
std::string num = std::string(++dll_name.begin(), dll_name.end());
int i = string_to_unsigned(num);
if(i == -1)
return "";
return resolve_symbol(finder, dll_name, i, s);
}
return resolve_symbol(finder, dll_name, procedure, s);
}
loaded_target_t *load_dll(coff_state_t &s, dll_finder_t &finder, std::string dll_name)
{
loaded_target_t *ptarget = s.get_target(dll_name);
if(ptarget == NULL)
{
std::cerr << ".";
// DLL not already loaded!
std::string fullname = finder.find(dll_name);
if(fullname.empty())
{
return NULL;
}
std::string path, file;
split_filename(fullname, path, file);
if(file.empty())
{
return NULL;
}
std::string module = make_uppercase(dll_name);
ptarget = &s.secondary[module];
ptarget->clear();
U1 *bin = load_target(path, file, *ptarget);
if(bin == NULL)
{
return NULL;
}
// OK, we just loaded a new DLL.
loaded_target_t &target = *ptarget;
coff_t *coff = (coff_t *)(bin + get_coff32(bin, target.size));
if(coff == NULL)
{
delete [] bin;
return NULL;
}
//--- process exports ---
if(coff->number_data_dirs == 0 || coff->data_dirs[0].rva == 0 || coff->data_dirs[0].size == 0)
; // no exports
else
{
std::map<U4, std::string> xexports;
edt_t *exports = (edt_t *)(bin + coff->data_dirs[0].rva);
U4 *functions = (U4 *)(bin + exports->rva_functions);
U4 *names = (U4 *)(bin + exports->rva_names);
U2 *ordinals = (U2 *)(bin + exports->rva_ordinals);
for(U4 x = 0; x < exports->number_of_functions; ++x)
{
U4 y = x + exports->ordinals_base;
xexports[y] = std::string("#") + int_to_string(y);
}
for(U4 x = 0; x < exports->number_of_names; ++x)
{
xexports[ordinals[x] + exports->ordinals_base] = std::string((char *)(bin + names[x]));
}
using namespace std;
//cout << "Exports:" << endl;
std::string newname;
for(std::map<U4, std::string>::iterator i = xexports.begin(); i != xexports.end(); ++i)
{
U8 offset = functions[i->first - exports->ordinals_base];
// note--offset may be 0 at this point. ignore that case.
// check for forwarders.
if(offset >= coff->data_dirs[0].rva && offset < coff->data_dirs[0].rva + coff->data_dirs[0].size)
{
// is a forwarder. we trust no DLL will forward in a cycle, e.g. a.dll -> b.dll -> a.dll.
// we only check that the target of the forward is NOT THE ROOT (because that would be a
// cycle and also, if the root forwards it, we won't know since we don't process forwarded
// exports from the root).
std::string t = std::string((const char *)(&bin[offset]));
std::string::iterator ix = t.begin();
while(ix != t.end())
{
if(*ix == '.')
break;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -