Chapter 5 blob stream

Source: Internet
Author: User
Tags 0xc0

B. Il

A. CS

Output

Table 5-1

The preceding example is the initial step for interpreting the method signature. This program is directly used as a prototype throughout this chapter. The newly introduced function will be appended to the end of the ABC function. The fillparamsarray and displaymethodsignature functions are added to the program.

To support the display of function signatures, the existence of parameter names is inevitable, as we can see in the output. Currently, the fillparamsarray method is filled with a string array with each parameter name. The preceding Code reads the param table and is re-used to fill the instance array paramnames.

The 8th members in the array row determine the size of the array. In contrast to the array starting from 0, the row number starts from 1. Therefore, the size of the array is increased by 1, so that the offset of the array is synchronized with the row number.

Finally, at the vertex of the function, there is an array called paramnames, which includes the name of the parameter to be displayed as part of the function signature. The essence of the above program is not the Function displaymethodtable, but the function displaymethodsignature.

In the displaymethodtable function, we started to read the entire method table. However, only the name and signature fields are used, and they store the method signature offset in the Blob stream.

After that, the displaymethodsignature function will be called. It has three parameters: the offset in the Blob heap and the offset in the param table (the location of the 1st parameter of this function) and the function name as a string. However, the offset Param is far-reaching only when the method signature identifies these parameters.

Table 5-2

As usual, 1st bytes are the number of bytes, and its value is 6, which is used in the first function pqr. The number of bytes is stored in the count variable. Then, create an array blob1 and the entire method signature -- the size is limited by the Count value and compiled into a byte array.

This method simplifies the process of effectively interpreting the method signature, because the array blob1 is used to store the signature, unlike the Blob for other content.

After count, it is one byte, which contains two different types of information. One involves the call conventions, and the other discusses the this pointer.

The firstfourbits variable retrieves the first four digits of the first 1st bytes, which confirms the call conventions.

Among the four bits, if the values of the first three bits are all 0, the call convention is the default one. If the value is 0x05, the call convention is vararg. The vararg call Convention simplifies the transmission of variable parameters.

The fourth group of four bytes identifies whether the this pointer is passed and whether the modifier explicit is used in the function. This pointer is not specified in a static function. The result is that the 6th-bit is off. As shown in the pqr function, it is marked as static.

On the other hand, the 6th bits of the function ABC are on, and it also uses the modifier explicit.

The 1st bytes of the method signature process the call conventions. The next byte provides the number of parameters used by the method. This value is stored in the paramcount variable and used to display all parameters.

The 3rd bytes provide the return type, which is assumed to be a simple type. The GetType function returns this type in string format. Here, we also assume that these parameters save simple value types.

The for loop repeats this process, depending on the number of parameters, so that all parameters obtained by the function are displayed. The GetType function is used to return the type, while the paramarray array is used to provide the array name. The signature will be displayed in readable form-this is the mechanism relied on

We have created too many assumptions about the method signature. Since the basic points have been well analyzed, let's create and compile a method based on the above basics. This method is used to decode the signature of the most complex method.

B. Il

The fillparamsarray, displaymethodtable, and displaymethodsignature functions are modified. Further, the Code specified below should be located after the GetType function, but before the closed braces of the class zzz.

A. CS

Output

Table 5-3

Let's start with the Il file first. We try to penetrate it as complicated as possible. Pqr function obtains five parameters: 1st parameters B, which are a simple type; 2nd parameters, namely AA, which are an out parameter; 3rd parameters, which are of the ZZZ type, 4th are ZZZ objects. However, this is not the last word in the method, because it can be made more complex.

In the fillparamsarray method, two string arrays named typerefnames and typedefnames are created, they are used to store the type names that appear in the typeref and typedef tables. The types created or involved in the method signature are stored in these tables. The usage of these two arrays is the same as that of paramarray.

In addition to the fillparamsarray, displaymethodtable, and displaymethodsignature functions, the remaining code is the same as that of the previous program. We also eliminate unnecessary code and other display features.

The displaymethodsignature method transmits the start offset of the method signature in the Blob stream. This is the most important parameter, because it cannot continue without the signature index. With this signature, the parameter name also needs to be displayed. Therefore, we have the index of the starting Param table as the 2nd parameter. Finally, the function name is displayed through 3rd and the last parameter.

The main purpose of this function is to return a string that exposes the method signature.

To achieve this goal, we have introduced multiple writeline statements. We split the code into several methods as much as possible so that they can be reused. However, on the other hand, when you make progress from one method to the next, you may be confused and frustrated. Even so, the good thing is that it can make the code easy to understand without the need to repeatedly.

Before we move to the new Code introduced in the program, there are still some urgent issues.

The 1st bytes of any entity in the Blob stream are the number of bytes. For many reasons, we always assume that the number is one byte, and our philosophy has not yet been questioned. However, the only obvious drawback is that byte is limited by its capacity, that is, it can only provide numbers in the range of 0 to 255, this is a total of 256 (8 power of 2) digits.

The confusion here is that if the signature size exceeds 255 bytes, it cannot be expressed in a single byte. It requires two bytes to provide a number in the range from 2 to 65535, a total of 65535 (16 power of 2) digits. At a specific time, even if this range fails to reach the goal. In this case, an integer is used to store numbers such as the 32 power of 2.

However, in most cases, this number is in the range of 0 to 127, and will be extended to 10000 s only in certain occasions. Therefore, it is useless to save an int to store the number, because it will bear a considerable amount of space loss. Therefore, the metadata designer decides to compress each byte before storing it in the metadata stream.

Table 5-4

This results in a smaller stream size. The resulting speed loss will significantly increase the additional time spent on compressing and Uncompressing these bytes.

Apply to the following method:

Table 5-5

Check the 1st bits of the 1st bytes. If it is 0, the following seven bytes represent the number. Therefore, a single byte is used for numbers ranging from 0 to 127 (or 0x00 to 0x7f.

Table 5-6

Because the number of bytes is usually in this range, it saves a lot of space. For a larger integer, the first 1st digits are switched to on. In this case, the system checks the subsequent bits. If this bit is 0, it indicates the following 14 digits that store the number. Therefore, from 128 to 16383 (2 ^ 14-1) or from 0x80 to 0x3fff can be provided by 14 digits.

Table 5-7

Finally, if 1st bits are set to 1 and 2nd bits are set to 1, 3rd bits are checked. If the 3rd-bit value is 0, the Count member can use 29 digits to store numbers until 0x1fffffff or 536870911.

Let's examine an example in the document in detail. Numbers such as 0x03 and 0x7f are stored in 8 bits. A number such as 128 or 0x80 -- it is greater than 127 -- will use 16 bits and be stored as 0x8080.

This is because 16th bits are set, 15th bits are cleared, and the remaining 14 BITs provide this value. 0x2e57 is stored as 0xae57 in the same way, while the maximum number 0x3fff provided by two digits is bfff. The number 0 x requires four bytes because it is greater than 0 x 3fff. It is stored as c0004000, where 31st bits are set and 29th bits are cleared.

Finally, the maximum number is expressed as 0x1fffffff In the compressed stream. It is stored as 0 xdfffffff. Another small problem is that we are checking the maximum bit. However, in the little endian system, the minimum bytes are first stored. This turns out to be an obstacle, because our goal is to read the high of the high byte.

Therefore, all values are stored in the reverse order, that is, the big endian system, where the values are first stored in high order. For example, if the number is 515, 3 is stored behind 2 in the little endian format, and 2 is stored behind 3 in the big endian format. Each time a value is read, a check is executed to determine whether the byte is compressed. If it is compressed, it needs to be decompressed.

The value stored in these bytes is an integer that indicates the size of the number of bytes obtained from the Blob area. Therefore, we need a function that accepts a byte array, the starting position or index of the byte, and an integer that stores uncompressed bytes. In addition, we want to store the returned values and the number of bytes used at the end.

Writing a function suitable for all these specifications is a daunting task. Therefore, we observe a program named metainfo, which is provided by the frameworksdk In the developer tool code. This example also shows the information in the metadata table. It is written in managed C ++. The only obstacle to this program is that it does not give us access to the original bytes in the table.

However, it provides rich information such as method signature. In the metainfo program, call the corsiguncompressdata method to extract bytes. We also decided to call our method based on the same name. This method obtains three parameters, namely, the Blob array, the offset starting from the signature in the array, and the final uncompressed output parameter, the returned value is exposed to check whether the compression uses 1, 2, or 4 bytes.

Now let's understand how the corsiguncompressdata method works.

The parameter index of the function helps to check the bytes in the Blob stream. Check the 1st bits to determine whether it is 0. Perform the and operation with 0x80. If the answer is 0, the last digit is 0 instead of 1. This indicates that the value is in the range of 0 to 127 and is stored in a single byte. We set the out parameter answer to the value included in the byte array. Then, we set the return value to 1, indicating that a single byte is used here. So far, everything has been doing well!

By performing the and operation on the byte and 0xc0 in the array, you can determine whether the last two bits are on. If the result is 0x80, the last bit is on, that is, the last bit is set, and the last 2nd bits are cleared. This only indicates that the next two bytes are stored, and thus the variable CB is set to 2.

Then, at the high level, by performing the and bit operation with 3f, the 15th and 14th bits are set to 0. The result shows that the six digits are shifted to the left, which occupies 8 to 15 digits. Then, they perform the or operation with the next byte in the byte array, so that the first 8 bits will be filled.

If this value exceeds the valid limit, the final check is used to determine whether the last two bits are set and whether the last 3rd bits are cleared. Therefore, bytes and 0xe0 perform the and bit operation. It is a bit combination mode-the last three digits are on. If answer is 0xc0, that is, the last two digits are on and the 3rd bits are off, it specifies that the signature size is limited to 4 bytes.

Because it is set to the big endian machine by default, the last three digits are marked as off, and the 24-digit shift to the right occupies the high position of this long integer. After that, we access the next byte of the byte array and shift it to 16 places to the right, which occupies the last 2nd bytes. The first byte only shifts the right eight bits, and the second byte does not move at all. Both perform or bit operations to get all the bytes.

The variable CB is then set to 4 because 4 bytes are used here. This compression method provides 29 available bits.

Table 5-8

Therefore, whenever an undecompressed method is called, the variable Index identifies the offset in the Blob array. The return value of this function is stored in the CB variable of displaymethodsignature.

After that, the CB value of the variable is added to the index variable, because there is no other method to determine the number of uncompressed bytes in the Blob stream. The variable count is initialized and stored in the value of the variable uncompressedbyte. In our example, the signature is 22 bytes long. Then, the following 22 bytes are only used to display the method signature in its original form.

We recommend that you pause and add about 120 parameters to the above function. This will increase the variable count to more than 127, and then you can easily verify the input. The uncompressed method performs a magic trick to set the CB value to 2.

As previously, the array. Copy function provides a clean copy of the signature bytes in the blob1 array, and the variable index is set to 0, because this is the start of the 1st bytes.

Bytes after the variable count indicate the call conventions. To identify the call conventions, the getcallingconvention function is used to decode the first byte and return the string value. Later, we will study the complexity of the call conventions.

The first item is Param count. The value 5 indicates that five parameters exist. This value is stored in the variable paramcount. Each time a byte is read, it is first extracted and then used.

The next byte is the return value. To decode the returned value, the getreturntype function is called. This function executes a specific job and then returns a string value. The last parameter of this function exposes the number of bytes used by the return type. In addition, like the previous one, this function transfers the index location starting from the Blob array and the returned byte.

The getreturntype function first checks whether the type is a simple type, that is, whether the value is less than or equal to 0x0e. If so, the GetType function is used to decode this simple type and set the variable CB to 1. However, at present, the number of returned bytes is 18. This number indicates a class. Therefore, it determines that this type is a class.

The Bytes after the return type provide the details of this class, which is stored as a symbol. These details of the class may be sent to any of the following three tables: typeref, typedef, and typespec.

Table 5-9

Compression is a famous saying in the metadata world. Thus, Mark -- indicates the table name and index, and is created in a completely compressed form. The first two bytes of the tag select the table to be indexed, and the remaining five digits are allocated the number of rows in the table. The number of digits remaining is 5, not 6, because the first byte checks whether the row number occupies 1 or 2 bytes.

Indexes of the three tables can be expressed as one byte until the row number is 32. However, if the row number is extended to 32, two digits are required to store the mark. Therefore, the row and table are first compressed, and then the mark is further compressed. In our example, the mark value is 8, and the first two digits are marked as 0. Therefore, it indexes the typedef table.

Table 5-10

For the next parameter, the index value is obtained by moving this mark two places to the right. As a result, this index is the first row of the typeref table, that is, the class zzz.

The opposite code is as follows:

First, we get a row index and move it two places to the left. Then, perform the or operation on the two bits of the table. At last, the value is compressed. The CB value is obviously CB1 + 1. It can be 1, 2, or 4. Code that translates a class type can be placed in a separate method to facilitate its reuse.

Now we have reached the core of the program.

It is a parameter after the return type. The for loop is used to iterate all parameters. Furthermore, all parameters are stored in the back-to-back mode with no bytes in the middle. The variable CB1 stores the number of bytes required by the parameter.

In a loop, the byte is first extracted, and then transmitted to the getelementtype function for decoding. This function returns parameters in the form of strings.

The getelementtype method obtains three items:

L an array of bytes, which is actually uncompressed bytes.

L an index named index, that is, the place where the previous bytes reside.

L out parameter.

This function calls other functions, depending on the actual value of bytes. As before, if the value is less than 0x0e, The GetType method will be called. If it is 0x12, the getclasstype function will be called and it will perform actual decoding.

The Code specified in the function is similar to the Code included in the getreturntype function. Here we retrieve it to simplify it to achieve maximum compression.

The variable index points to the byte 0x0a, which indicates a long integer. This is the type of the first parameter. The next byte is 16. If the displayed type is 16, it indicates that it is a reference type and there is a metadata mark after it. When the keyword ref or out is used in C #, or when the keyword out is used in Il, a reference type is available. After the keyword out is displayed, the next byte, that is, the metadata mark, will be checked to discover the index of the table and row.

The ref parameter is followed by two parameters of the type ZZZ and yyy. Therefore, the getelementtype function obtains a byte and identifies it. It can be a simple type or a complex type. If the value is 20, this type is an array.

Table 5-11

For arrays, the implementation of the getarraytype function has four identical functions.

The first four bytes of the array signature are the data type of the array. Generally, the getelementtype function applies to the identification type. Value 4 indicates that this type is an integer. The next byte is the dimension (rank) of the array ). Dimension indicates the dimension of an array. In this example, the array has five dimensions.

The next byte task of numsizes is to measure the size of several dimensions. In our example, three of the five dimensions have sizes. The last two dimensions have no size at all. This is completely reasonable.

Because the following two bytes contain the actual size of each dimension, an integer array with the dimension equal to numsizes is created. If the index range of the array is 6 to 8, the size is 3. This is because both the lower bound and the upper bound are included. In B. Il, 3rd dimensions are specified as 3 to 9, so the size is 7.

This field is also called the size of an array. If no dimension is specified, the size is exactly 0. If it is a separate integer with no lower bound or upper bound, the byte size is the dimension of the array. In this example, sizearray is first filled, and then an array boundsarray including the lower bound is created.

The Byte after sizes is the lower bound of the array. In our example, It is 3. This number is different from the size of the byte. These bytes are lower bounds of the dimension, and their size has been specified in advance. If the value is 0, no lower bound is specified.

The 3rd arrays and 6 appear together, but in the Il file, it is reflected as 3. This is because the number is signed, so they need to be decompressed first. In this way, the system checks 1st bits. Because it is not set, these bits are moved by only one bit to the right, thus trunking 1st bits.

Table 5-12

If 1st bits are set, depending on the number of compressed bytes, some bits are marked as off. These two arrays are displayed as the health check using the for loop ). They also use the bounds variable as the primary key.

Now, we need to place the dimension of the array.

First, the upper bound is determined by the computer. It is calculated from the lower bound + size-1. The upper and lower bounds should be placed in the returned strings. To achieve this, first check the bounds array to check whether it is 0 and whether the size array is non-0. If the result of this condition is true, both the upper and lower bounds have a simple size. This If statement processes the last comma.

If both arrays contain 0, both upper and lower bounds exist. Therefore, they are displayed with three commas.

Finally, as the space for saving features, all specified dimensions at the end are not placed in these two arrays. The difference between rank and numsizes determines the number of empty commas required in the array. Placing 0 at the end of two arrays is absurd and meaningless.

Local variable Signature

B. CS

> Csc B. CS/unsafe

A. CS

Output

Table 5-13

The above example does two things:

L first, it overwrites the code for reading the metadata table.

L Secondly, it explains the signature in a high sense.

Each time a local variable is created in the function, a row is added to the standalonesig table. Because the main function does not have local variables, no rows will be added to the table. This table has a separate field, namely signature. No other metadata table references it, nor does it reference any other metadata table.

File B. CS is embedded with "insecure" functions. Therefore, it must use the unsafe option. There is also a variable I in this function. Before explaining the signature, let's take a look at the changes brought about by function ABC.

In the signed program, the file pointer is set at the offset of  which has a data directory item for the CLL header. This method works well in both the Il compiler and the C # compiler, because both products start at the offset of 128 in the PE file.

In our work, most compilers use a standard program, which runs at any time under DOS. However, it is not like hosting a C ++ compiler. Therefore, we cannot assume that the PE Header always starts at the offset of 128.

Therefore, to avoid all these assumptions, the PE offset is stored in variable II, and then the file pointer is set at the beginning of baseofcode. This location is always 44 bytes at the beginning of the PE file.

The CLR header is still a non-elastic and fixed offset from the start position. After locating the file pointer in the CLR header, you can retrieve the size and RVA.

On the other hand-taken for granted-RVA works under the assumption that "file alignment and section alignment are two different things. However, if they happen to be the same, RVA must be calculated as the physical offset on the disk. Therefore, no additional computation is performed. The same RVA may be used as the memory offset and disk offset.

The code used for stream name is also changed. The 2nd while loop continues until a non-0 value is encountered. However, when the length of any stream can be divisible by 256, we will find a great deal of trouble. In this example, the 1st bytes will be 0, so the stream offset, size, and name will be followed by one. In this way, the loop continues until a non-zero value or position attribute becomes divisible by 4.

The fillparamsarray function has not been modified. Therefore, we will not display them any more. This can also be used to decode an array.

The displaystandalonesigtable function only calls the displayvariablessignature function with BLOB indexes. Therefore, the displayvariablessignature function is used to decode the signature.

Table 5-14

Table 5-15

As usual, the first byte is count. Based on this count. The bytes in the Blob area are copied to an array named blob1.

2nd bytes, including the call conventions, now provides value 7, specifying the local variable signature. If the value of 2nd bytes is not 7, an error is returned. The 3rd bytes are the number of local variables. The for loop iterates on it. As before, the getelementtype function does all the hard work. Because the value of 3rd bytes is 8, The GetType function returns an integer.

Now, we add a different variable type to observe the changes and output in bytes.

B. CS

Output

Some rows are omitted below

In File B. CS, there is a pointer to an integer, which leads to the Type 15 in the signature. Because this is a pointer type, we call it the getpointervalue method. This is similar to the type of a class, where the next byte is the actual data type. In this example, it is an integer. We call the getelementtype method to read the next byte and interpret this type. The symbol * is added to a string to indicate a pointer.

B. CS

Yyy;

Output

Count = 4 bytes 7 1 17 12 yyy

The next byte is an object with the value type yyy. This is similar to the type of the class, rather than the type number 17. This calls the getvaluetype function, which works similar to the getclasstype function.

B. CS

YYY *;

Output

Count = 5 bytes 7 1 15 17 12 YYY *

The getelementtype function works effectively. From the output, this is quite obvious, and the variable is described as a pointer to the type yyy. Type 15 is a pointer type followed by the next number 17. It is a value type. This will get the mark 12, which represents the class yyy.

B. CS

Output

Count = 7 bytes 7 2 18 16 69 16 8 XXX, pinned [byref] int

Variables a and I are described in the above program. The type of variable A is XXX. The first two bytes are 18 and 16. The number 69 is used for the pinned type, followed by the byref type 16, followed by 8, which is the final Integer type.

Pinning is a medium-it clearly instructs you not to move a hosted object through the runtime, because a pointer references it here. By default, the runtime is allowed to move any object in the memory.

B. CS

Object;


Output

Count = 3 bytes 7 1 28 system. Object

Type 28 is reserved for system. object. Similarly, the typedreference class has numbers 22, intptr 24, and uintptr 25.

B. CS

Some rows are omitted below

Output

Count = 5 bytes 7 3 22 24 25 system. typedreference, system. intptr, system. uintptr

Field Signature

B. cpp

Some rows are omitted below

> Cl/clr B. cpp

A. CS

Output

Table 5-16

In this program, fillparamsarray is used to fill the typedefnames and typerefnames arrays. Therefore, comment on displaystandalonesigtable and call the other two functions. Fillparamsarray. If it is omitted in the program, it may be input as follows:


Some rows are omitted below

Furthermore, the displayelementtype function will be changed. As a result, it will become attractive again. In addition, the readstringindex and getstring functions are displayed because they are temporarily requisitioned by this program.

File B. cpp is written by hosting C ++, so its file extension is CPP. The only required function is main. Note that M is in lower case. In addition to this main function, there is also a field or global variable P, which is a pointer to the function. It obtains four parameters, namely an int, a char, a float, and a double. Finally, it returns an int. Use the C ++ compiler CL and options/CLR to compile the above program and create a. Net file.

In the C # program, as we started earlier, we noted the displaystandalonesigtable method and called the displayfieldstable method as an alternative. The displayfieldssignature method is used to upload the index in the Blob stream. The field name and signature are displayed. If the subsequent signature is a field signature, the 1st bytes in the Blob heap must be 0x06. The getelementtype method returns only this value.

Table 5-17

For a while, we had to track the position in the Blob array. This is not suitable because each field is represented by its own rows in the fields table. The actual code is included in the getelementtype function, because the type encoding of the pointer to a function is 27 or 0x1b.

We are quite satisfied with our interpretation of the concept of "pointer to a function"-using hosted C ++, a language we have been committed. The getpointertofunctiontype function is called. The pointer to the function type is located after the method signature. The first byte is the call Convention byte, because it is a typical starting position of a method signature.

This byte has a value of 2, which indicates a standard call convention. In the past, Windows programs often used it. The getcallingconvention method checks whether the call agreed values are C, standard, thiscall, or fastcall.

The next byte is the number of parameters obtained by this method. In this example, It is 4. The first is the return type of the method. The return type starts with the number 32. In this document, the custom modifier is called a "Custom modifier" or an optional modifier (for example, in our example ), or start with a fixed/required modifier 31. When the modifier is optional, the compiler saves the ignore option. However, if the modifier is required, it will become part of the compiler's consideration.

There is a mark after the modifier, which we have described in detail earlier. The getelementtype function is called again. Perform additional checks on required and optional modifiers. With the help of this mark, the decoded class will be displayed. The getelementtype method is called again to decode the next byte. This may be necessary for a simple byte.

Implement a loop for a large number of parameters. It rents the service of the getelementtype function to process custom modifiers, because the custom modifiers can also be located before these parameters. The first two parameters of the pointer are int and char. The 1st parameters have a custom modifier, but the 2nd parameters do not.

MemberrefTable

B. cpp

A. CS

Output

Table 5-18

The memberref table is filled with the details of each function called in this program. In a hosted C ++ program, three periods in the Code are used to indicate the variable quantity of parameters. However, it is necessary to specify the 1st parameters of the function, which will be subsequently called with any number of parameters.

In the above program B. cpp, function ABC will be called in the main function, with 1, 2, or 3 parameters. This will add three notes to the memberref table.

After the fillparamsarray function is called, The displaymemberreftable function is explicitly called. By using this function, all methods are displayed. However, for the purpose of interpretation, we can only consider the method ABC, not the constructor. The displaymethodsignature method is used to decode the signature in the Blob heap. Therefore, index and name are provided for it.

We deliberately avoided encoding indexes and used the integer type for parameters. Therefore, the value 8 will flash. The number 32 is an optional modifier after the mark, so that it can be ignored.

The ABC function of the first call has two integers. One is optional and the other is required. It is located in the 2nd rows of the method reference table. The last three bytes of the signature will also be displayed.

We have made the assumption that the signature will end with two 8 s. However, the number 65 can be seen in the middle. This byte is called Sentinel. It indicates that all subsequent parameters are optional. Therefore, three periods are displayed when the Sentinel byte appears.

Row 4 shows the Sentinel byte 65 after two 8 s. This is because the called ABC function has two optional integers. The last row contains a separate integer. Therefore, there is no optional parameter. The result is that Sentinel bytes do not exist.

During encoding, we found that we may introduce code for the Sentinel byte in our getelementtype method. This is because the loop depends on the variable paramcount.

Not all. Given the Sentinel bytes in the getelementtype function also leads to an increase in the number of parameters.

Sentinel, which handles the code we introduced, is absolutely direct. Reduce the cycle variables one by one. Then we add three periods to the string S. Next, we increase the variable index by 1 because the next byte will be read. Finally, we use the continue statement to return to the starting position of the for loop.

This will be a completely healthy option to change the index of the For Loop, from paramcount to the number of bytes in the signature.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.