Tulip 2021 Game Assistive Technology Intermediate Class (1)

Read utf8 name with code

Insert image description here

Insert image description here

Insert image description here

Insert image description here

Insert image description here

The code block starting from line 33 to line 43 in the picture above is the UniCodeToAscii function.

Byte array search for UTF-8 string

The newer CE version now supports searching for UTF-8 strings:

Insert image description here

Older versions of CE cannot search for UTF-8 strings. There was a way at that time. We can modify the string type in the above picture to a byte array type:

Insert image description here

Insert image description here

Insert image description here

Insert image description here

Insert image description here

UTF-8 strings can also be found by searching the byte array in the picture above:

Insert image description here

Now we are using CE7.2, a relatively new version, which supports UTF-8, so we can directly search for the string.


Use CE and xdbg to analyze object names

Insert image description here

Insert image description here

Early CE cannot find UTF-8 strings. Here you can use this tool to convert our ordinary UTF-8 strings into a byte set, and then search for the byte set in CE.

Search the monster name to find the offset of the monster name, but there may be many results. Which one is the selected monster around us?
At this time, you need to filter by changing the name:

Insert image description here

Insert image description here

Insert image description here

If you find that the names of the monsters around you have not changed, then delete the selected names (such as the dichotomy), continue to select most of the names to modify, and repeat this step.

Insert image description here

If it still does not affect the name of the monster around you, delete it and continue to select all the remaining names for modification:
Insert image description here

Note, when modifying the name, do not move the character or hit the monster to avoid refreshing the monsters on the map and causing the data to expire; in addition, you can use the mouse to select the nearest monster (or select yourself first, and then select the monster), and switch Check whether the name on the selected monster's health panel has changed (as shown in the picture above, the name on the panel has changed, but the name on the model has not changed).

Insert image description here

Insert image description here

Insert image description here

Select yourself first, then select the nearest monster. If the name on the monster panel has changed, delete the remaining parts, leaving only the affected parts, and continue to modify by dichotomy (if there are few, you can filter half and half from back to front) , until the address of the currently selected monster name is determined, and then you can find its offset.

Insert image description here

Insert image description here

It is best for us to analyze the address in xdbg:

Insert image description here

Insert image description here

Set the next 4-byte access breakpoint at the address in the memory window and break it directly. You can see that eax is 2D05A2C8. Let’s take a look at the source of eax:

Insert image description here

Insert image description here

We find that [ebp+8] is equal to 0, so eax=[ecx+5c], which is the first offset.

Insert image description here

We check in the memory window, ecx+5c happens to be the address where 2D05A2C8 is stored, so ecx+5c is the first offset of the monster name;
And the second offset To move (find the source of ecx), you have to go to the upper level to see (double-click the return address through the stack window):

Insert image description here

Insert image description here

That is, 74F857 on the upper level calls 53B8E0 to get the monster name CALL.

Insert image description here

The ecx we are looking for comes from the return value of the CALL 67B6A0, that is, [return value of CALL 67B6A0 + 5C] is the monster name and address, so the focus now is to enter the CALL 67B6A0 and analyze the return value of the CALL.

Since this CALL is relatively complex, we first follow up the call point of the CALL and analyze where it returns the eax return value;
First, we F8 steps through the CALL to see if the content at [eax+5c] is the name of the monster:

Insert image description here

It shows that there is no problem with our finding method (the position of analysis). Okay, let's run the game again, break the CALL at the 74F84A address, and then press F7 to follow up to analyze the return value eax. After entering, we keep pressing F8 to go Chasing where eax is returned:

Insert image description here

The picture above shows that the location is wrong. It must not be returned from the place in the picture above (otherwise the monster's name can be read). Let's continue to press F8:

Insert image description here

Looking at the picture above, it also has an offset of 0x18:

Insert image description here

After testing, 0x18 is indeed an offset of the monster's name. Then let's take a look at where 0F26EF20 comes from. We can look at - the minus sign goes back step by step to see where the eax of 0F26EF20 comes from.

Insert image description here

This eax comes from the return value of CALL 6F6020.

Insert image description here

This CALL 6F6020 feels like going into the monster array to take out the monster object (you can seeds:[edx+eax*4+4] the disassembly form of a global array or a static array), a bit like this It feels like we make a mark at the beginning of the CALL.

This CALL feels a bit familiar to us. In Lesson 29 of the beginner class, we analyze the attributes of the role object:

Insert image description here

Insert image description here

Insert image description here

In the CALL 4D4DB0 query object, there is a CALL 4D4BB0 in this CALL:

Insert image description here

This CALL 4D4DB0 is very similar to the CALL 6F6020 we just analyzed.

Insert image description here

Insert image description here

Comparative analysis, it seems that they are all printed in the same pattern, and the similarity is very high. However, the addresses of the two CALLs are different. From the code analysis, they are basically the same.

At present, we still return to the analysis of CALL 67B6A0, which means that this place may be the place where the monster array is traversed. After traversing, the monster object is returned. Basically, the returned object +0x18+5C is the monster name. The address;
And we disconnected at the 74F84F address and found that the return value eax is always a fixed value F0FFCF8 (and F0FFCF8-0x18 happens to be the return value F0FFCE0 of the inner layer CALL 6F6020 in the above picture) , even if we change the perspective, there are more monster objects in the visible range, and the return value eax remains unchanged. It may be because this is the selected monster object, so let's change the monster and even select a monster in the distance. As a result, we found that the return value eax remained unchanged, which was quite strange. The returned value was always the same value, which required further analysis after downloading it.

Next, let’s take a look at the parameters passed to CALL 6F6020, what they are, and what they are used for:

Insert image description here

We go to the current esp in the memory window, and then view it with a 64-bit hexadecimal integer. This is to analyze whether the value is the ID of the monster:

Insert image description here

Then use CE to search for the 8-byte value 3ACFD370000012B:

Insert image description here

I found that there is only one place with this value. It is not yet known whether this value is the ID of the monster (only guessing).

Insert image description here

It is possible that CALL 6F6020 uses the monster's ID to query the name of the monster. Let's analyze it further.

Let’s use CE to search where the pointer eax=0F20E4F0 is stored:

Insert image description here

We found that there were many search results, so we entered CALL 0067B6A0 at the address 0074F84A:

Insert image description here

We found that the parameter 2 passed in changes every time CALL 006F6020 is disconnected in the stack window above, but ECX does not change.

Analysis from the perspective of LUA functions

In fact, there is another way to quickly analyze the LUA function registered through the game:

Insert image description here

Insert image description here

Insert image description here

Insert image description here

In the UnitName function, if the selected object is passed in, you can also follow it here.

Insert image description here

Let's take a look at the return value of eax, because this is a LUA function and will eventually return a name. As shown in the figure above, the player's name is returned at this time.

We first delete the breakpoint, let the game run, select a monster, then break at the beginning of the LUA function in the picture above, break it again and see what the target it returns is.

We can also run macros in the chat window in the game/run UnitName("target"), prepare the LUA command as shown below:

Insert image description here

Since the monster keeps attacking me, the LUA function is not easy to stop here, so we need to select the target without being attacked (you can choose a slightly farther one):

Insert image description here

Go to the value of eax in the memory window, and you can see that the target name is returned from CALL 4FD0E0. The name and offset of the monster must be found in this CALL.

Insert image description here

We interrupt at this CALL, and then enter the lua command in the game/run UnitName("target"), and press Enter to interrupt at this CALL. At this time, eax=2D1AEF90, we write it down, and then continue F8 single step to CALL 4FD0E0:

Insert image description here

You can see that ecx=2D1AEF90 at this time will be passed into CALL 4FDDE0 as a parameter. Let’s follow up in the CALL and see where the name comes from:

Insert image description here

Continue to follow:

Insert image description here

The monster name should come from CALL 72A000, let’s follow up:

Insert image description here

Insert image description here

At this point, ecx=2D1AEF90 is still our monster object:

Insert image description here

Insert image description here

Two offsets for the monster's name came out at once.

The LUA interface of UnitName is to display the name of the target we selected (the parameter is "target"). Of course, it will not actually be displayed. If you want to display it, you can enter it/run print(UnitName("target")) :

Insert image description here

Insert image description here

If you select yourself, the character name will be displayed:

Insert image description here

The same goes for selecting NPC, it is very simple to use LUA commands.

Another analysis path (that is, +0x18) should also be possible. Specifically, we will continue to analyze this path in the future. The difficulty is that the 6F6020 CALL is more complicated and there are many array operations in it.

Review monster name offsets

Let’s take the NPC fifty cents as an example:

Insert image description here

After searching, modify the name. At this time, we select ourselves, and then select Wu Mao. Check the character's health panel. You can see that the NPC Wu Mao's name has been modified. The name on the head of the NPC character model has changed to Wu 23 ( Be sure not to look at the name above the character's head), and the one on the panel changes to 50 cents and 11. We have to refer to what is displayed on the panel here.

Insert image description here

We changed 50 cents 11 to 50 cents 12 and determined the address of the monster name found by CE.

Let’s search again for the address where this name is stored:

Insert image description here

Insert image description here

In this way, its first offset +5C is found, and then we can continue to search for the value of esi, or we can display the disassembler to see if there are any higher-level offsets nearby:

Insert image description here

Insert image description here

You can see that it has another offset of +964.

Insert image description here

Next we use xdbg to continue the analysis:

Add a dot and a number zero after wow.exe, that iswow.exe+32A27F, you can directly go to the address in xdbg:

Insert image description here

We open the Memory 2 window and go to the address of esi:

Insert image description here

As shown in the figure above, copy the content starting at the address where esi is located. This is a very important feature. The content A34D90 stored in the address value of esi at this time is the key to judging whether it is an object, although it cannot Used to determine whether it is a monster object, but if A34D90 is the address of the virtual function table vftable, then this esi is most likely an object;
Because the object generally has a virtual function table at its first address. vftable, of course, is not absolute, but if the first address of the monster object or game object in the game is a virtual function table vftable, then they are an object. Whether it is, let's check and analyze it in memory 3 of xdbg: < /span>

Insert image description here

Insert image description here

You can see that both addresses are standard function headers.

Insert image description here

Insert image description here

In this way, it can be determined that A34D90 is a virtual function table vftable, or a virtual function pointer.

Insert image description here

In this case, we get two offsets of the monster name.

By using this virtual function pointer to determine that it is an object, we don't need to continue searching. We just need to find these two offsets, one +964 and one +5C, and finally we can read the monster's name.

Be sure not to look at the name above the character's head, otherwise you won't be able to find it. Look at the information on the panel.


Analyze object arrays with CE and xdbg

Insert image description here

Understand the virtual function table

Insert image description here

Create dialog-based MFC applications.

We create a new monster class directory (filter) in the project file directory and create a monster class to simulate monster objects:

Insert image description here

Insert image description here

Insert image description here

Insert image description here

Of course, this simulated monster array object is different from the one in our game, because it is directly a global variable. It has no offset and is directly a base address.

Insert image description here

Open the exe file with x32dbg, go to the MessageBeep function and interrupt. After running, click the button and it will interrupt here:

Insert image description here

Double-click the return address in the stack window in the lower right corner to return to our code:

Insert image description here

Insert image description here

Insert image description here

You can see the monster array directly here. In the memory window, we look at the object of the monster array. There is a comment vftable in its first address (of course there is no such comment in the game), which is actually the virtual function table pointer:

Insert image description here

Through this virtual function table pointer 0132898C, entering this address is the virtual function table:

Insert image description here

Each of these is actually the address of a function:

Insert image description here

Most of them are member functions in the CDialog base class that we inherited. We chose it to inherit in order to simulate and analyze the virtual function table:

Insert image description here

The virtual function pointers of a certain kind of monster are generally the same, so we can also use this virtual function table to distinguish different types of objects. For example, the virtual function table pointers of the player and the monster may be different. If they are really If they are of the same monster class, they may be the same, and many functions may also be in this virtual function table.

This is very helpful for us to analyze objects, because once we get a similar address and go in, we can find that this may be the head of an object, because there may be a large number of functions like this in the head of such an object. pointer.

Let’s take a look at the player objects (character objects) in the game:

Insert image description here

Insert image description here

Now eax is our role object. Let's go to 00A326C8 and take a look, because theoretically 00A326C8 is the so-called virtual function table pointer:

Insert image description here

Insert image description here

Each of these is the head of a function, so when we distinguish an object, if its head is a virtual function table pointer, then it has this very obvious feature, and 00A326C8 is the virtual function table address. (That is, the virtual function table pointer), and we see that there are many member functions in the character object, some are used for drawing, and some are used to obtain certain data (blood volume, attributes, etc.).

We need to see that the object has this characteristic. Our main purpose in this lesson is to understand the monster object or the player object. If it is an object, its first address is a function, which allows us to determine the found one. Thing is an object, such as a monster object or a player object.

You can simply know that if the first address of the object is entered, and all the large areas inside are the addresses of functions, you can be sure that it is an object;
That is to say, This pointer can help us determine that what we find is the first address of the object, because the first address of the object has this characteristic.

Insert image description here

Insert image description here

Insert image description here

This 0x1372C88 is the base address of the monster array, and 0x1D0 is the size of the monster class.

Insert image description here

Insert image description here

We can see from the two pictures above that the two objects monster array [0] and monster array [3] have the same virtual function table pointer 0136898C because they are the same class.

Insert image description here

Insert image description here

We see that there are large 0s under the virtual function table pointers of these objects. This is because we have not written data for the members in the object. We assign values ​​to some of the members for comparison:

Insert image description here

Insert image description here

Insert image description here

Here is a brief introduction to xdbg conditional breakpoints:

Insert image description here

Insert image description here

Insert image description here

Conditional breakpoints are generally places where downloads are frequently interrupted, such as window procedures. Multiple judgment conditions can also be added by combining logical AND &&, logical OR ||, and logical NOT!.

Analyze array of objects

Insert image description here

Let's go in from 0x60C1F0 and use xdbg to analyze the monster object array.

Insert image description here

If we want to analyze the monster array, we can find the monster object from the monster's name, and slowly chase the monster array. In addition, we can also chase the monster array through the source of the player object.
As we said before, monsters, player objects, NPCs, and dropped ground items in most games are generally placed in the same array or linked list, so the player object is It is returned from the CALL 0x601CF0, but is this player object also included in the monster array?
So we can find this monster array from the source of the player pointer, so we have to find it from the CALL 0x60C1F0.

Open the character information and stop at the beginning of the CALL. We keep pressing F8 to see where the last returned eax comes from:

Insert image description here
Insert image description here

Insert image description here

If it is our player object, there will be an obvious feature, that is, whether [player pointer + 0D0] + 174 is the player's armor value, so the return value eax comes from the query object CAL of 004D4DB0. Now we basically It can be determined that this CALL is the source of the player object, which means that the deeper CALL is our character object returned from 004D4DB0.

Insert image description here

The value 00A326C8 in the first address of the player object is actually the first address of the virtual function table mentioned before. All the functions stored in 00A326C8 are independent functions:

Insert image description here

Let’s go to the 4D4DB0 location to see where the return value eax is returned from:

Insert image description here

We have analyzed the parameters of the CALL 4D4DB0 before. We suspect that the first and second parameters are ID1 and ID2, which are used to query the object array. We use these two parameters to query the returned object, and That is to say, other monster objects should also be returned through this CALL.
Let’s continue to follow the CALL:

Insert image description here

Insert image description here

You can see that the player object comes from the CALL 4D4BB0, so the key array may still be in this layer of CALL.

Insert image description here

Insert image description here

But we talked about this CALL before. It is a very clean CALL. No other functions are called in it. You can see from the picture above that there is a loop in it. There is a high probability that it is doing a traversal in the loop. .

Let’s first take a look at the parameters of the CALL 4D4BB0:

Insert image description here

The first parameter is 0xC, and the second parameter edx=039FF6B0 is the address of ebp-8. Go to this address from the memory window (as shown in the figure above as 039FF6B0), and you can see that a 0xC and a 0 are stored in it.

Insert image description here

Break at the end of the 4D4BB0 function, and then run it continuously. It is observed that the return value eax remains unchanged most of the time, but occasionally changes. Let's take a look at what is inside [2CFD05C8+D0]+174 (as shown in the memory window in the lower left corner of the above figure) content), you can see that this place is obviously not the armor value, it may be something else. We compare this object with the value in the player object.

We disconnect at the end of the 4D4BB0 function, mainly because we suspect that monster objects or other objects will be returned from this place, but we cannot directly disconnect at the 4D4BB0 location, otherwise the game will continue to disconnect until it freezes, so we need Break on the previous layer, then break again here, and press Run to break to this place.

Insert image description here

Insert image description here

Let's go to the memory window[eax+D0]+174 to take a look. If it is a player, this place will be the armor value. At this time, it proves that 2C318C50 is the character object. If it is returned, it is other If it is a value, it may be a monster object:

Insert image description here

We found that if it is not a player (eax=2D4E79F0 at this time, and the content at the [2D4E79F0+D0]+174 address is 0 as shown in the picture above), there is no armor value in this place.

Let's compare the contents of the character object 2C318C50 and another object 2D4E79F0 in the memory window:

Insert image description here

Insert image description here

We can see that the virtual function table pointers of different objects are also different, indicating that they belong to different types. So what exactly is this object 2D4E79F0?
We previously analyzed the name of the monster (/run print(UnitName("target"))), and at that time we analyzed two offsets, +964 and +5C:

Insert image description here

We use this formula[[eax+964]+5C] to see if this object 2D4E79F0 has a monster name:

Insert image description here

This confirms that the object 2D4E79F0 is a monster object, so one returned from 4D4BB0 is a character object, and the other is a monster object. Of course, there may also be other objects (mounts, ground items, minerals, herbs, etc.). After all, it is okay. Which types of objects are returned requires further analysis. From the current point of view, there are at least these two types of objects.

Next we have to see where this monster object or player object comes from in the CALL 4D4BB0:

Insert image description here

We can see that ecx is an obvious key. This edx should be the base address of the array, and this eax should be the subscript of a certain array.
So what is ecx+1c? Let's go back to the previous level and take a look at the source of this ecx:

Insert image description here

From the picture above, we can see that ecx ultimately comes from ds:[edx+8], and the source of edx is related to the array subscript eax. The content of the source of eax, ds:[D439BC], is from the lower left corner. The memory window shows 0, so edx=[ecx+eax4]=[ecx+04]=[ecx], so the key is Back to this ecx=fs:[2C], we have also talked about this fs:[2C] before, you can just think of it as an ordinary memory address (the TEB of this thread is mapped in the memory address), we substitute fs:[2c] into the formula, so edx=[ecx]=[fs:[2c]], that is, in the end ecx=[[fs:[2c]]+8] we get a new expression.

Insert image description here

Let’s verify it:

Insert image description here

In the memory window, the address we go to through the expression [[fs:[2c]]+8] is 136FC3A0, and the value of the ecx register happens to be 136FC3A0.

Let’s first enter the CALL 4D4BB0. We use an equivalent replacement. Replace eax=[ecx+24] in the figure below with the expression of ecx [[fs:[2c]]+8], and we get eax=[[[fs:[2c]]+8]+24], let’s follow up the 4D4BB0 CALL to verify it (remember to break here to get the correct fs:[2c] in the main thread of the game): a>

Insert image description here

You can see that the value of eax is 1F, and the value of the formula[[[fs:[2c]]+8]+24] is indeed 1F as shown in the picture above. This proves that this formula is correct. Let’s continue to execute: < /span>

Insert image description here

Prove that the expression edx=[ecx+1c]=[[[fs:[2c]]+8]+1c] is also correct. The edx here is the key point, which is the address of the array.

Let's continue reading:

Insert image description here

The decimal value of 1F, the value of eax, is 31. From hereand eax, esi, we can see that 31 may be the maximum array upper limit. It truncates the excess value beyond 31 ( For example, in the picture above, we changed esi=0000000c to esi=1234567c to verify the effect of the and eax, esi instruction), and finally got eax=c to ensure that it is within the range of the number of array elements.
(After verification, remember to restore the value of esi to 0000000c to ensure the normal environment of the program)

Insert image description here

Let’s continue with equivalent substitutions:

Insert image description here

Insert image description here

After executing the instructionand eax, esi, the value of eax is 0c, so in fact eax is the passed parameter 1 (eax may be a subscript):

Insert image description here

Insert image description here

Insert image description here

The formula in the picture above may be the final monster array. We replace parameter 1 with 0. The final formula is[[[[fs:[2c]]+8]+1c]+8+0*0c]. The calculation is 2D4F5960, and the content in 2D4F5960 is:

Insert image description here

This is indeed an object, which proves that our formula can indeed obtain the objects in the array.

Let's continue looking at the code:

Insert image description here

Insert image description here

Insert image description here

It is found that eax has not been rewritten in the following code, so the value of eax is ultimately returned.

Let's try the monster name again (the offset of the name is +964, then +5c):

Insert image description here

If it is a monster, then the name of the monster can be read. If it is a player or other type of object, it may not be read. From the values ​​calculated in the above figure, we can see that it can be read correctly. This The first element in the array at index 0 is probably our monster object.

Insert image description here

Insert image description here

Remember to break here so that in the main thread of the game, the fs:[2c] obtained is correct and the correct result can be obtained through the above formula (otherwise it cannot be accessed and must be in the main thread) ).

Insert image description here

If the subscript is changed to 2, we can see that there may be an object here, but the name cannot be displayed (the object has no name or the name is not at this offset position). We can be sure that it is not a monster object. We modify the formula to see if it is What object:

Insert image description here

Insert image description here

It looks like our player object. If it is a player object, we modify the formula to see the player's armor value:

Insert image description here

Insert image description here

As a result, the armor value cannot be seen, indicating that this object is neither our player object nor a monster object, and may be other types of objects.

Insert image description here

Insert image description here

We found that using this formula (when the index is 2 at this time), we can also get the name of the NPC, indicating that the NPC and the monster may be of the same class. To prove this, let's check the object's virtual function table pointer:

Insert image description here

Insert image description here

The A34D90 in the picture above is the NPC's virtual function table pointer.

Insert image description here

We know that the index 0 belongs to the monster, and its virtual function table pointer is also A34D90:

Insert image description here

Explain that monsters and NPCs are of the same category.

Except for index 0 and index 2, the rest are not, and there is no content until index 1A:

Insert image description here

Insert image description here

The object with index 1A is the nameless NPC, and the virtual function table pointer of this object is also A34D90.

Insert image description here

The monster list we analyzed is actually not complete (it involves linked lists). Here we will talk about part of it first, and later we will continue to analyze the monster list in detail.
There is also a loop that traverses the linked list below the code, which should also contain other monster objects. It is traversed through this loop:

Insert image description here

Insert image description here

Insert image description here

This fs:[2C] is a special memory address, which is the address where the TEB of this thread is mapped in memory:

Insert image description here

Insert image description here

Insert image description here


Analyze the object array linked list part

How is a linked list created in the C++ environment, and let's take a look at how the linked list data is stored in memory.

Definition of linked list

Insert image description here

Insert image description here

Insert image description here

Insert image description here

Insert image description here

What form does the linked list data take in memory?

Insert image description here

// CreateList.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include <iostream>

//    操作系统 win7 64
//    编译环境 Visual Stuido 2017

#include<stdio.h>
#include<malloc.h>
#include<stdlib.h>

typedef int ElementType;        //    定义数据类型,可根据需要进行其他类型定义
//class								//    链表节点的定义
typedef struct ListNode {
    
    
	ElementType  Element;        //    数据域,存放数据
	int data1;
	int data2;
	ListNode* Next;        // 链表指针   指向下一个链表节点
	float x,y,z;
}Node, *PNode;

//    链表创建函数定义
PNode CreateList(void) {
    
    
	int len;    //    用于定义链表长度
	int val;    //    用于存放节点数值
	PNode PHead = (PNode)malloc(sizeof(Node));    //    创建分配一个头节点内存空间//头节点相当于链表的哨兵,不存放数据,指向首节点(第一个节点)
	if (PHead == NULL)    //    判断是否分配成功
	{
    
    
		printf("空间分配失败 \n");
		exit(-1);
	}

	PNode PTail = PHead;    //    链表的末尾节点,初始指向头节点
	PTail->Next = NULL;    //    最后一个节点指针置为空
	printf("请输入节点个数:");
	scanf_s("%d", &len);        //    输入节点个数
	for (int i = 0; i < len; i++) {
    
    

		PNode pNew = (PNode)malloc(sizeof(Node));    //    分配一个新节点
		if (pNew == NULL) {
    
    
			printf("分配新节点失败\n");
			exit(-1);
		}
		printf("请输入第 %d 个节点的数据:", i + 1);
		scanf_s("%d", &val);    //    输入链表节点的数据

		pNew->Element = val;    //    把数据赋值给节点数据域
		PTail->Next = pNew;    //    末尾节点指针指向下一个新节点
		pNew->Next = NULL;        //    新节点指针指向为空
		PTail = pNew;    //    将新节点复制给末尾节点        
	}
	printf("创建链表成功\n");
	return PHead;    //    返回头节点
}

//    主函数 
int main() {
    
    
	PNode List = CreateList();    //创建一个指针,使其指向新创建的链表的头指针  
	printf("List链表头=%p\n", List);
	getchar();
	return 0;
}

Insert image description here

Insert image description here

You can see that the data in the head of the List is uninitialized. Only Next is initialized to point to the first node, and the value of the Element of the first node is the 111 data we just entered, and this first node is The Next expansion of the point points to the second node:

Insert image description here

Insert image description here

Finally, when the Next pointer points to NULL, the traversal of the entire linked list ends when we traverse here.

Then we analyze the disassembly of the linked list in xdbg:

Insert image description here

Add MessageBeep(1); to facilitate positioning.

Insert image description here

Insert image description here

Insert image description here

We looked at the contents of the linked list header 7BCAE0 in the memory window and found that they were all unknown data. We need to find the pointer field in the linked list header:

Insert image description here

Insert image description here

Insert image description here

So the address of the first node is at the pointer field position of +C offset, and its address is 7BF950:

Insert image description here

Insert image description here

Insert image description here

So the value 7BF9A8 at the offset +C position in the first node is the second node:

Insert image description here

Insert image description here

Insert image description here

Insert image description here

This is how the linked list data structure is stored in memory.

Insert image description here

The picture above is a loop that traverses an object linked list. This linked list structure is used in the game. The object array we talked about before, the element inside is a class object, and the members in the object may be a linked list. Let’s put it this way , that is to say, there is a member in the monster object which is the head of the linked list.

Insert image description here

Insert image description here

The pointer field Next of the head node points to the first node (the value in the data field is 3), the pointer field Next of the first node points to the second node (the value in the data field is 2), and the pointer field Next of the first node points to the second node (the value in the data field is 2). The pointer field Next of the first node points to the third node (the value in the data field is 1), the pointer field Next of the third node points to NULL, and the traversal ends here.

Insert image description here

Insert image description here

Insert image description here

Insert image description here

Guess you like

Origin blog.csdn.net/zhaopeng01zp/article/details/132550006