@@ -39,12 +39,16 @@ public Patcher(Stream stream)
3939 /// <returns>A list of the URLs and Digest keys</returns>
4040 private static List < PatchTargetInfo > FindPatchableElements ( Stream file )
4141 {
42+ long start = Stopwatch . GetTimestamp ( ) ;
43+
4244 file . Position = 0 ;
4345 using IELF ? elf = ELFReader . Load ( file , false ) ;
4446 file . Position = 0 ;
4547
46- BinaryReader reader = new ( file ) ;
47-
48+ //Buffer the stream with a size of 4096
49+ BufferedStream bufferedStream = new ( file , 4096 ) ;
50+ BinaryReader reader = new ( bufferedStream ) ;
51+
4852 // The string "http" in ASCII, as an int
4953 int httpInt = BitConverter . ToInt32 ( "http"u8 ) ;
5054
@@ -56,65 +60,56 @@ private static List<PatchTargetInfo> FindPatchableElements(Stream file)
5660 //Found positions of `cook` in the binary
5761 List < long > cookiePositions = new ( ) ;
5862
59- foreach ( ISection section in elf . Sections )
63+ long read = 0 ;
64+
65+ //Create an array twice the size of the data we are wanting to check
66+ Span < byte > arr = new byte [ 8 ] ;
67+ while ( reader . Read ( arr ) == arr . Length )
6068 {
61- // Assume a word size of 4, i would use `ProgBitsSection<T>.Alignment`
62- // but that seems to be unrelated to the actual alignment and offset chosen for the string constants specifically (the section with all the strings is marked as 32-byte alignment[?????])
63- // so we'll just assume 4 byte alignment since that seems universal here
64- int wordSize = 4 ;
65- ulong sectionLength ;
66- switch ( section )
67- {
68- case ProgBitsSection < ulong > progBitsSectionUlong :
69- file . Position = ( long ) progBitsSectionUlong . Offset ;
70- sectionLength = progBitsSectionUlong . Size ;
71- break ;
72- case ProgBitsSection < uint > progBitsSectionUint :
73- file . Position = progBitsSectionUint . Offset ;
74- sectionLength = progBitsSectionUint . Size ;
75- break ;
76- default :
77- continue ;
78- }
79-
80- int read = 0 ;
81-
82- byte [ ] buf = new byte [ wordSize ] ;
83- //While we are not at the end of the file, read each word in
84- while ( read < ( int ) sectionLength - wordSize )
69+ long ? found = null ;
70+ for ( int i = 0 ; i < 5 ; i ++ )
8571 {
86- read += file . Read ( buf ) ;
72+ int check = BitConverter . ToInt32 ( arr [ i .. ( i + 4 ) ] ) ;
8773
88- int bufInt = BitConverter . ToInt32 ( buf ) ;
89-
90- //If they are equal, we found an instance of HTTP
91- if ( bufInt == httpInt )
74+ if ( check == httpInt )
9275 {
93- //Mark the position of the found instance
94- possibleUrls . Add ( reader . BaseStream . Position - wordSize ) ;
76+ possibleUrls . Add ( read + i - 4 ) ;
77+ found = read + i - 4 ;
78+ break ;
9579 }
9680
97- if ( bufInt == cookieStartInt )
81+ // ReSharper disable once InvertIf
82+ if ( check == cookieStartInt )
9883 {
99- cookiePositions . Add ( reader . BaseStream . Position - wordSize ) ;
84+ cookiePositions . Add ( read + i - 4 ) ;
85+ found = read + i - 4 ;
86+ break ;
10087 }
10188 }
102- }
10389
90+ //Seek 4 bytes after the position we started at, or 4 bytes after the starting index of a match
91+ reader . BaseStream . Seek ( found == null ? read + 4 : found . Value + 4 , SeekOrigin . Begin ) ;
92+
93+ read += arr . Length ;
94+ }
95+
10496 List < PatchTargetInfo > foundItems = new ( ) ;
105- FilterValidUrls ( file , possibleUrls , reader , foundItems ) ;
106- FindDigestAroundCookie ( file , cookiePositions , reader , foundItems ) ;
97+ FilterValidUrls ( reader , possibleUrls , foundItems ) ;
98+ FindDigestAroundCookie ( reader , cookiePositions , foundItems ) ;
99+
100+ long end = Stopwatch . GetTimestamp ( ) ;
101+ Console . WriteLine ( $ "Detecting patchables took { ( double ) ( end - start ) / ( double ) Stopwatch . Frequency } seconds!") ;
107102 return foundItems ;
108103 }
109104
110- private static void FilterValidUrls ( Stream file , List < long > foundPossibleUrlPositions , BinaryReader reader , List < PatchTargetInfo > foundItems )
105+ private static void FilterValidUrls ( BinaryReader reader , List < long > foundPossibleUrlPositions , List < PatchTargetInfo > foundItems )
111106 {
112107 bool tooLong = false ;
113108 foreach ( long foundPosition in foundPossibleUrlPositions )
114109 {
115110 int len = 0 ;
116111
117- file . Position = foundPosition ;
112+ reader . BaseStream . Position = foundPosition ;
118113
119114 //Find the first null byte
120115 while ( reader . ReadByte ( ) != 0 )
@@ -137,13 +132,16 @@ private static void FilterValidUrls(Stream file, List<long> foundPossibleUrlPosi
137132 //Keep reading until we arent at a null byte
138133 while ( reader . ReadByte ( ) == 0 ) len ++ ;
139134
140- file . Position = foundPosition ;
135+ //Remove one from length to make sure to leave a single null byte after
136+ len -= 1 ;
137+
138+ reader . BaseStream . Position = foundPosition ;
141139
142140 //`len` at this point is the amount of bytes that are actually available to repurpose
143141 //This includes all extra null bytes except for the last one
144142
145143 byte [ ] match = new byte [ len ] ;
146- if ( file . Read ( match ) < len ) continue ;
144+ if ( reader . Read ( match ) < len ) continue ;
147145 string str = Encoding . UTF8 . GetString ( match ) . TrimEnd ( '\0 ' ) ;
148146
149147 if ( str . Contains ( '%' ) ) continue ; // Ignore printf strings, e.g. %s
@@ -159,11 +157,11 @@ private static void FilterValidUrls(Stream file, List<long> foundPossibleUrlPosi
159157 }
160158 }
161159
162- private static void FindDigestAroundCookie ( Stream file , List < long > foundPossibleCookiePositions , BinaryReader reader , List < PatchTargetInfo > foundItems )
160+ private static void FindDigestAroundCookie ( BinaryReader reader , List < long > foundPossibleCookiePositions , List < PatchTargetInfo > foundItems )
163161 {
164162 foreach ( long foundPosition in foundPossibleCookiePositions )
165163 {
166- file . Position = foundPosition ;
164+ reader . BaseStream . Position = foundPosition ;
167165
168166 byte [ ] cookieBuf = new byte [ 8 ] ;
169167 //If we didnt read enough or what we read isnt "cookie\0\0"
@@ -176,7 +174,7 @@ private static void FindDigestAroundCookie(Stream file, List<long> foundPossible
176174 const int checkSize = 1000 ;
177175
178176 //Go back half the check size in bytes (so that `cookie` is in the middle)
179- file . Position -= checkSize / 2 ;
177+ reader . BaseStream . Position -= checkSize / 2 ;
180178
181179 byte [ ] checkArr = new byte [ checkSize ] ;
182180 Span < byte > toCheck = checkArr . AsSpan ( ) . Slice ( 0 , reader . Read ( checkArr ) ) ;
@@ -209,7 +207,7 @@ private static void FindDigestAroundCookie(Stream file, List<long> foundPossible
209207 foundItems . Add ( new PatchTargetInfo
210208 {
211209 Length = len ,
212- Offset = file . Position - checkSize + start ,
210+ Offset = reader . BaseStream . Position - checkSize + start ,
213211 Data = str ,
214212 Type = PatchTargetType . Digest ,
215213 } ) ;
0 commit comments