A few days ago a colleage and I were looking at a bug:

Issue: Connectors with a policy containing ja char’s in etc do not start up. Loading the policy in a browser, it is broken.

Okay, so let’s unpack that statement. Japanese characters in our configuration is causing issues.

To understand how to correct this, we have to understand a few background concepts.

When we generate an installer for any of the products we support (windows, mac, android, linux etc). We inject a custom configuration into the installer and sign the installer. The custom configuration includes input from our end users. This can be as simple as the name of a policy.

This specific defect was occurring with our windows agent.

To get to the bottom of this we will need to understand the PE format. Below I’m using pedump on a .net binary that a candidate submitted as a a sample solution to a programming problem.

λ pedump ~/development/candidates/<snip>/src/domain/bin/Debug/domain.dll

=== MZ Header ===

                     signature:                     "MZ"
           bytes_in_last_block:        144          0x90
                blocks_in_file:          3             3
                    num_relocs:          0             0
             header_paragraphs:          4             4
          min_extra_paragraphs:          0             0
          max_extra_paragraphs:      65535        0xffff
                            ss:          0             0
                            sp:        184          0xb8
                      checksum:          0             0
                            ip:          0             0
                            cs:          0             0
            reloc_table_offset:         64          0x40
                overlay_number:          0             0
                     reserved0:          0             0
                        oem_id:          0             0
                      oem_info:          0             0
                     reserved2:          0             0
                     reserved3:          0             0
                     reserved4:          0             0
                     reserved5:          0             0
                     reserved6:          0             0
                        lfanew:        128          0x80

=== DOS STUB ===

00000000: 0e 1f ba 0e 00 b4 09 cd  21 b8 01 4c cd 21 54 68  |........!..L.!Th|
00000010: 69 73 20 70 72 6f 67 72  61 6d 20 63 61 6e 6e 6f  |is program canno|
00000020: 74 20 62 65 20 72 75 6e  20 69 6e 20 44 4f 53 20  |t be run in DOS |
00000030: 6d 6f 64 65 2e 0d 0d 0a  24 00 00 00 00 00 00 00  |mode....$.......|

=== PE Header ===

                     signature:             "PE\x00\x00"

# IMAGE_FILE_HEADER:
                       Machine:        332         0x14c  x86
              NumberOfSections:          3             3
                 TimeDateStamp:    "1970-01-01 00:00:00"
          PointerToSymbolTable:          0             0
               NumberOfSymbols:          0             0
          SizeOfOptionalHeader:        224          0xe0
               Characteristics:       8450        0x2102  EXECUTABLE_IMAGE, 32BIT_MACHINE, DLL

# IMAGE_OPTIONAL_HEADER32:
                         Magic:        267         0x10b  32-bit executable
                 LinkerVersion:                      8.0
                    SizeOfCode:       8192        0x2000
         SizeOfInitializedData:       1536         0x600
       SizeOfUninitializedData:          0             0
           AddressOfEntryPoint:      15998        0x3e7e
                    BaseOfCode:       8192        0x2000
                    BaseOfData:      16384        0x4000
                     ImageBase:    4194304      0x400000
              SectionAlignment:       8192        0x2000
                 FileAlignment:        512         0x200
        OperatingSystemVersion:                      4.0
                  ImageVersion:                      0.0
              SubsystemVersion:                      4.0
                     Reserved1:          0             0
                   SizeOfImage:      32768        0x8000
                 SizeOfHeaders:        512         0x200
                      CheckSum:          0             0
                     Subsystem:          3             3  WINDOWS_CUI
            DllCharacteristics:      34112        0x8540  DYNAMIC_BASE, NX_COMPAT, NO_SEH
                                                          TERMINAL_SERVER_AWARE
            SizeOfStackReserve:    1048576      0x100000
             SizeOfStackCommit:       4096        0x1000
             SizeOfHeapReserve:    1048576      0x100000
              SizeOfHeapCommit:       4096        0x1000
                   LoaderFlags:          0             0
           NumberOfRvaAndSizes:         16          0x10

=== DATA DIRECTORY ===

  EXPORT        rva:0x       0   size:0x        0
  IMPORT        rva:0x    3e30   size:0x       4b
  RESOURCE      rva:0x    4000   size:0x      324
  EXCEPTION     rva:0x       0   size:0x        0
  SECURITY      rva:0x       0   size:0x        0
  BASERELOC     rva:0x    6000   size:0x        c
  DEBUG         rva:0x       0   size:0x        0
  ARCHITECTURE  rva:0x       0   size:0x        0
  GLOBALPTR     rva:0x       0   size:0x        0
  TLS           rva:0x       0   size:0x        0
  LOAD_CONFIG   rva:0x       0   size:0x        0
  Bound_IAT     rva:0x       0   size:0x        0
  IAT           rva:0x    2000   size:0x        8
  Delay_IAT     rva:0x       0   size:0x        0
  CLR_Header    rva:0x    2008   size:0x       48
                rva:0x       0   size:0x        0

=== SECTIONS ===

  NAME          RVA      VSZ   RAW_SZ  RAW_PTR  nREL  REL_PTR nLINE LINE_PTR     FLAGS
  .text        2000     1e84     2000      200     0        0     0        0  60000020  R-X CODE
  .rsrc        4000      324      400     2200     0        0     0        0  40000040  R-- IDATA
  .reloc       6000        c      200     2600     0        0     0        0  42000040  R-- IDATA DISCARDABLE

=== RESOURCES ===

FILE_OFFSET    CP  LANG     SIZE  TYPE          NAME
     0x2258     0     0      716  VERSION       #1

=== IMPORTS ===

MODULE_NAME      HINT   ORD  FUNCTION_NAME
mscoree.dll         0        _CorDllMain

=== VERSION INFO ===

# VS_FIXEDFILEINFO:
  FileVersion         :  1.0.0.0
  ProductVersion      :  1.0.0.0
  StrucVersion        :  0x10000
  FileFlagsMask       :  0x3f
  FileFlags           :  0
  FileOS              :  4
  FileType            :  2
  FileSubtype         :  0

  VarFileInfo         :  [ 0x7f, 0x4b0 ]

# StringTable 007f04b0:
  Comments            :  ""
  CompanyName         :  ""
  FileDescription     :  "domain"
  FileVersion         :  "1.0.0.0"
  InternalName        :  "domain"
  LegalCopyright      :  "Copyright ©  2012"
  LegalTrademarks     :  ""
  OriginalFilename    :  "domain.dll"
  ProductName         :  "domain"
  ProductVersion      :  "1.0.0.0"

=== Packer / Compiler ===

  MS Visual C# / Basic .NET

The PE format instructs the windows OS on how to execute the program. The interesting part of this format is the .rsrc section that describes resources that are embedded in the binary. This is where we inject the custom configuration. With the resource injection we also have to update the size of the resource.

Let’s take a closer look. Below is a dump of the resources in the binary. It shows a single resource with a size of 716, and name of #1.

λ pedump -R ~/development/candidates/<snip>/src/domain/bin/Debug/domain.dll
=== RESOURCES ===

FILE_OFFSET    CP  LANG     SIZE  TYPE          NAME
     0x2258     0     0      716  VERSION       #1

This is where understanding encodings and the size becomes important.

Here’s an example in ruby:

λ irb
irb(main):001:0> "M".size
=> 1
irb(main):002:0> "M".bytesize
=> 1
irb(main):003:0> "科".size
=> 1
irb(main):004:0> "科".bytesize
=> 3

So when calculating the size of the resource that is injected into the executable we need to use the byte size, not just the number of characters.

Which makes for a nice easy one line fix.

-      write_resource_size(offset_to_size, content.size, injected_file)
+      write_resource_size(offset_to_size, content.bytesize, injected_file)

Happy hacking!

comments powered by Disqus