ssize_t calculate_signature_space(pesign_context *ctx) { int rc; SECItem sig = { 0, }; rc = generate_spc_signed_data(&sig, &ctx->cms_ctx); if (rc < 0) { err: fprintf(stderr, "Could not generate signature.\n"); exit(1); } data_directory *dd = NULL; rc = pe_getdatadir(ctx->outpe, &dd); if (rc < 0) goto err; ssize_t ret = sig.len + dd->certs.size + sizeof(win_certificate) - available_cert_space(ctx); //free(sig.data); return ret; }
int cert_iter_init(cert_iter *iter, Pe *pe) { iter->pe = pe; iter->n = 0; iter->certs = 0; iter->size = -1; data_directory *dd; int rc = pe_getdatadir(pe, &dd); if (rc < 0) return -1; void *map; size_t map_size; map = pe_rawfile(pe, &map_size); if (!map) return -1; iter->certs = map + dd->certs.virtual_address; if (dd->certs.virtual_address) { iter->size = dd->certs.size; } return rc; }
int pe_populatecert(Pe *pe, void *cert, size_t size) { int rc; data_directory *dd = NULL; rc = pe_getdatadir(pe, &dd); if (rc < 0) return rc; if (size != dd->certs.size) return -1; void *mem = compute_mem_addr(pe, dd->certs.virtual_address); if (!mem) return -1; memcpy(mem, cert, size); uint64_t max_size = pe->maximum_size; uint32_t new_space; uint32_t page_size = sysconf(_SC_PAGESIZE); pe_extend_file(pe, 0, &new_space, page_size); uint64_t new_max_size = pe->maximum_size; mem = compute_mem_addr(pe, 0); msync(mem, new_max_size, MS_SYNC); new_max_size -= max_size; pe_shorten_file(pe, new_max_size); return 0; }
ssize_t available_cert_space(pesign_context *ctx) { cert_iter iter; int rc = cert_iter_init(&iter, ctx->outpe); if (rc < 0) return -1; data_directory *dd; rc = pe_getdatadir(ctx->outpe, &dd); if (rc < 0) return -1; ssize_t totalsize = dd->certs.size; ssize_t foundsize = 0; void *data; ssize_t datalen; while (1) { rc = next_cert(&iter, &data, &datalen); if (rc <= 0) break; foundsize += datalen; } return totalsize - foundsize; }
int pe_alloccert(Pe *pe, size_t size) { int rc; data_directory *dd = NULL; pe_clearcert(pe); uint32_t new_space = 0; rc = pe_extend_file(pe, size, &new_space, 8); if (rc < 0) return rc; rc = pe_getdatadir(pe, &dd); if (rc < 0) return rc; void *addr = compute_mem_addr(pe, new_space); /* We leave the whole list empty until finalize...*/ memset(addr, '\0', size); dd->certs.virtual_address = compute_file_addr(pe, addr); dd->certs.size += size; return 0; }
off_t __pe_updatemmap(Pe *pe, size_t shnum __attribute__((__unused__))) { /* This needs to write back the whole file: * 1) mz/pe/pe-o headers * 2) section headers and sections * 3) data directory table and data directories * * We also need to check if the signature is valid and if not, * make sure it's not in the data directory. */ struct mz_hdr *mzhdr = pe->state.pe.mzhdr; struct pe_hdr *pehdr = pe->state.pe.pehdr; if (pe->flags & PE_F_DIRTY) { off_t offset = 0; memcpy(pe->map_address + offset, mzhdr, sizeof(*mzhdr)); offset += le32_to_cpu(mzhdr->peaddr); memcpy(pe->map_address + offset, pehdr, sizeof(*pehdr)); } /* it's not dirty any more, so clear the flag. */ pe->flags &= ~PE_F_DIRTY; /* flush back to disk */ char *msync_start = ((char *) pe->map_address + (~(sysconf(_SC_PAGESIZE) -1 ))); data_directory *dd = NULL; int rc = pe_getdatadir(pe, &dd); if (rc < 0) { /* XXX set an error here */ return -1; } char *msync_end = (char *)dd + sizeof(*dd); msync(msync_start, msync_end - msync_start, MS_SYNC); #warning this is not done yet. //struct section_header *sh = __get_last_section(pe); size_t dd_size = sizeof (*dd) / sizeof (dd->exports); data_dirent *dde = &dd->exports; for (unsigned int i = 0; i < dd_size; i++, dde++) { if (dde->size != 0) { char *addr = compute_mem_addr(pe, dde->virtual_address); msync(addr, dde->size, MS_SYNC); } } return 0; }
int pe_clearcert(Pe *pe) { int rc; data_directory *dd = NULL; rc = pe_getdatadir(pe, &dd); if (rc < 0) return rc; if (dd->certs.virtual_address != 0) { pe_freespace(pe, dd->certs.virtual_address, dd->certs.size); memset(&dd->certs, '\0', sizeof (dd->certs)); } return 0; }
int pe_populatecert(Pe *pe, void *cert, size_t size) { int rc; data_directory *dd = NULL; rc = pe_getdatadir(pe, &dd); if (rc < 0) return rc; if (size != dd->certs.size) return -1; void *addr = compute_mem_addr(pe, dd->certs.virtual_address); if (!addr) return -1; memcpy(addr, cert, size); msync(addr, size, MS_SYNC); return 0; }
int generate_digest(pesign_context *ctx, Pe *pe) { void *hash_base; size_t hash_size; struct pe32_opt_hdr *pe32opthdr = NULL; struct pe32plus_opt_hdr *pe64opthdr = NULL; PK11Context *pk11ctx; unsigned long hashed_bytes = 0; int rc = -1; if (!pe) { fprintf(stderr, "pesign: no output pe ready\n"); exit(1); } struct pe_hdr pehdr; if (pe_getpehdr(pe, &pehdr) == NULL) { fprintf(stderr, "pesign: invalid output file header\n"); exit(1); } void *map = NULL; size_t map_size = 0; /* 1. Load the image header into memory - should be done * 2. Initialize SHA hash context. */ map = pe_rawfile(pe, &map_size); if (!map) { fprintf(stderr, "pesign: could not get raw output file address\n"); exit(1); } pk11ctx = PK11_CreateDigestContext(ctx->cms_ctx.digest_oid_tag); if (!pk11ctx) { fprintf(stderr, "pesign: could not initialize digest\n"); exit(1); } PK11_DigestBegin(pk11ctx); /* 3. Calculate the distance from the base of the image header to the * image checksum. * 4. Hash the image header from start to the beginning of the * checksum. */ hash_base = map; switch (pe_kind(pe)) { case PE_K_PE_EXE: { void *opthdr = pe_getopthdr(pe); pe32opthdr = opthdr; hash_size = (uintptr_t)&pe32opthdr->csum - (uintptr_t)hash_base; break; } case PE_K_PE64_EXE: { void *opthdr = pe_getopthdr(pe); pe64opthdr = opthdr; hash_size = (uintptr_t)&pe64opthdr->csum - (uintptr_t)hash_base; break; } default: goto error; } PK11_DigestOp(pk11ctx, hash_base, hash_size); /* 5. Skip over the image checksum * 6. Get the address of the beginning of the cert dir entry * 7. Hash from the end of the csum to the start of the cert dirent. */ hash_base += hash_size; hash_base += pe32opthdr ? sizeof(pe32opthdr->csum) : sizeof(pe64opthdr->csum); data_directory *dd; rc = pe_getdatadir(pe, &dd); if (rc < 0 || !dd) goto error; hash_size = (uintptr_t)&dd->certs - (uintptr_t)hash_base; PK11_DigestOp(pk11ctx, hash_base, hash_size); /* 8. Skip over the crt dir * 9. Hash everything up to the end of the image header. */ hash_base = &dd->base_relocations; hash_size = (pe32opthdr ? pe32opthdr->header_size : pe64opthdr->header_size) - ((uintptr_t)&dd->base_relocations - (uintptr_t)map); PK11_DigestOp(pk11ctx, hash_base, hash_size); /* 10. Set SUM_OF_BYTES_HASHED to the size of the header. */ hashed_bytes = pe32opthdr ? pe32opthdr->header_size : pe64opthdr->header_size; struct section_header *shdrs = calloc(pehdr.sections, sizeof (*shdrs)); if (!shdrs) goto error; Pe_Scn *scn = NULL; for (int i = 0; i < pehdr.sections; i++) { scn = pe_nextscn(pe, scn); if (scn == NULL) break; pe_getshdr(scn, &shdrs[i]); } sort_shdrs(shdrs, pehdr.sections - 1); for (int i = 0; i < pehdr.sections; i++) { hash_base = (void *)((uintptr_t)map + shdrs[i].data_addr); hash_size = shdrs[i].raw_data_size; PK11_DigestOp(pk11ctx, hash_base, hash_size); hashed_bytes += hash_size; } if (map_size > hashed_bytes) { hash_base = (void *)((uintptr_t)map + hashed_bytes); hash_size = map_size - dd->certs.size - hashed_bytes; PK11_DigestOp(pk11ctx, hash_base, hash_size); } SECItem *digest = PORT_ArenaZAlloc(ctx->cms_ctx.arena, sizeof (SECItem)); if (!digest) goto error_shdrs; digest->type = siBuffer; digest->data = PORT_ArenaZAlloc(ctx->cms_ctx.arena, ctx->cms_ctx.digest_size); digest->len = ctx->cms_ctx.digest_size; if (!digest->data) goto error_digest; PK11_DigestFinal(pk11ctx, digest->data, &digest->len, ctx->cms_ctx.digest_size); ctx->cms_ctx.pe_digest = digest; if (shdrs) free(shdrs); PK11_DestroyContext(pk11ctx, PR_TRUE); return 0; error_digest: PORT_Free(digest->data); error_shdrs: if (shdrs) free(shdrs); error: PK11_DestroyContext(pk11ctx, PR_TRUE); fprintf(stderr, "pesign: could not digest file.\n"); exit(1); }