Skip to content

Instantly share code, notes, and snippets.

@grassmunk
Created April 13, 2020 21:31
Show Gist options
  • Save grassmunk/c92c96c3bf921f38efdc003138e0082e to your computer and use it in GitHub Desktop.
Save grassmunk/c92c96c3bf921f38efdc003138e0082e to your computer and use it in GitHub Desktop.
import sys
import os
import collections
import PIL.Image
import svgwrite
import pathlib
import shutil
import subprocess
import argparse
import logging
import logging.handlers
import configparser
import xml.etree.ElementTree as ET
from pathlib import Path
from PIL import Image
from configparser import ConfigParser
from fontTools import ttLib
import binascii
import struct
# Extract ICO from ICL/DLL files
f = open(sys.argv[1],'rb')
ani_file = f.read()
f.close()
ani_bytes = bytearray(ani_file)
print("Parsing NE DLL/ICL")
group_type = { 3: 'RT_ICON', 14 :'RT_GROUP_ICON' }
e_lfanew = struct.unpack('<I',ani_bytes[60:64])[0]
ne_header_char = ani_bytes[e_lfanew:e_lfanew+2].decode()
# https://www.codeproject.com/Articles/16178/IconLib-Icons-Unfolded-MultiIcon-and-Windows-Vista
# https://hwiegman.home.xs4all.nl/fileformats/exe/WINHDR.TXT
if ne_header_char == 'NE':
print("NE Header: {}".format(ne_header_char))
ne_rsrctab = struct.unpack('<H',ani_bytes[e_lfanew+36:e_lfanew+36+2] )[0] + e_lfanew
rscAlignShift = struct.unpack('<H',ani_bytes[ne_rsrctab:ne_rsrctab+2] )[0]
resource_table = {'rscAlignShift':rscAlignShift, 'rscTypes': [], 'rscEndTypes' : 0, 'rscResourceNames': [], 'rscEndNames': 0}
print("Offset from 0 to NE header (e_lfanew): {}".format(e_lfanew))
print("Parsing Resource Tables (ne_rsrctab) at {} ({})".format(ne_rsrctab, hex(ne_rsrctab)))
TNAMEINFO = []
ptr = ne_rsrctab+2 #Advance ptr to TYPEINFO
rttypeid = 1
while rttypeid != 0 and rttypeid < 24:
tmp_ba = ani_bytes[ptr:]
rttypeid = struct.unpack('<H',tmp_ba[0:2] )[0] & 0x7FFF
if rttypeid == 0 or rttypeid > 24:
continue # At the end of the type info array exit
rtresourcecount = struct.unpack('<H',tmp_ba[2:4] )[0]
tmp_ba = ani_bytes[ptr+8:]
print("Type ID {} has {} records".format(group_type[rttypeid], rtresourcecount, ptr+8, hex(ptr+8)))
size = 0
for x in range(0, rtresourcecount):
TNAMEINFO.append( {
'rttypeid' : rttypeid,
'rnOffset' : struct.unpack('<H',tmp_ba[size+ 0:size+2])[0] << rscAlignShift,
'rnLength' : struct.unpack('<H',tmp_ba[size+ 2:size+4])[0],
'rnFlags' : struct.unpack('<H',tmp_ba[size+ 4:size+6])[0],
'rnID' : struct.unpack('<H',tmp_ba[size+ 6:size+8])[0] & 0x7FFF,
'rnHandle' : struct.unpack('<H',tmp_ba[size+ 8:size+10])[0],
'rnUsage' : struct.unpack('<H',tmp_ba[size+ 10:size+12])[0]
} )
size = size + 12 #Skip ahead these entries
ptr = ptr + size + 8 # Skip to the next TYPEINFO
ptr = ptr + 2 # rscEndTypes
tmp_ba = ani_bytes[ptr:]
names = 0
length = 1
#Resource Names
RESOURCENAMES = []
#print("resource names ptr {} {}".format(ptr, hex(ptr)))
while length != 0:
length = tmp_ba[names]
RESOURCENAMES.append(tmp_ba[names+1:names+1+length].decode())
names = names + tmp_ba[names] + 1
resource_table['rscResourceNames'].extend(RESOURCENAMES)
resource_table['rscTypes'].extend(TNAMEINFO)
ICONS = []
for GRPICONDIRENTRY in resource_table['rscTypes']:
if GRPICONDIRENTRY['rttypeid'] == 14: #RT_GROUP_ICON
try:
name = RESOURCENAMES[GRPICONDIRENTRY['rnID']]
except (KeyError, IndexError):
name = sys.argv[1]
pass
idReserved = struct.unpack('<H',ani_bytes[GRPICONDIRENTRY['rnOffset']+0:GRPICONDIRENTRY['rnOffset']+2])[0]
idType = struct.unpack('<H',ani_bytes[GRPICONDIRENTRY['rnOffset']+2:GRPICONDIRENTRY['rnOffset']+4])[0]
idCount = struct.unpack('<H',ani_bytes[GRPICONDIRENTRY['rnOffset']+4:GRPICONDIRENTRY['rnOffset']+6])[0]
#print("Offset: {}, idType: {}, idCount: {}, Name: {}".format(hex(GRPICONDIRENTRY['rnOffset']), idType, idCount, name))
tmp_grp = ani_bytes[GRPICONDIRENTRY['rnOffset']+6:]
for x in range(0, idCount):
rtIcon = {
'bWidth' : tmp_grp[0], # Width, in pixels, of the image
'bHeight' : tmp_grp[1], # Height, in pixels, of the image
'bColorCount' : tmp_grp[2], # Number of colors in image (0 if >=8bpp)
'bReserved' : tmp_grp[3], # Reserved
'wPlanes' : struct.unpack('<H',tmp_grp[4:6])[0], # Color Planes
'wBitCount' : struct.unpack('<H',tmp_grp[6:8])[0], # Bits per pixel
'dwBytesInRes' : struct.unpack('<L',tmp_grp[8:12])[0], # how many bytes in this resource?
'nId' : struct.unpack('<H',tmp_grp[12:14])[0] # RT_ICON rnID
}
for RT_ICON in resource_table['rscTypes']:
if RT_ICON['rttypeid'] == 3 and RT_ICON['rnID'] == rtIcon['nId']:
icon_file = bytearray(2) + struct.pack('<H',1) + struct.pack('<H',1)
ICONENTRY = tmp_grp[0:12] + struct.pack('<L', 22)
icon_bitmap = ani_bytes[RT_ICON['rnOffset']:RT_ICON['rnOffset']+rtIcon['dwBytesInRes']]
#print(ICONENTRY)
if rtIcon['bColorCount'] == 0: rtIcon['bColorCount'] = 256
filename = "{}_{}_{}x{}x{}.ico".format(name, GRPICONDIRENTRY['rnID'], rtIcon['bWidth'], rtIcon['bHeight'], rtIcon['bColorCount'])
print("Creating:", filename)
#f = open(filename,"wb")
#f.write(icon_file+ICONENTRY+icon_bitmap)
#f.close()
ICONS.append({'filename': filename, 'ICON': icon_file+ICONENTRY+icon_bitmap})
tmp_grp = tmp_grp[14:]
for i in ICONS:
print(i['filename'], len(i['ICON']))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment