吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 13234|回复: 82
收起左侧

[2021] 2021腾讯游戏安全技术复赛pc客户端安全wp

  [复制链接]
Qfrost 发表于 2021-4-12 11:57

题目说明:

  1. shootgame是一个游戏,hack.exe是游戏shootergame的一个外挂程序。
  2. 运行shootgame游戏,运行hack.exe,成功执行外挂功能并分析外挂实现过程。
  3. 实现一个与hack.exe的功能相同的,但是游戏逻辑原理不同的外挂程序。(游戏逻辑原理不同:指外挂程序对读写游戏数据结构或代码的攻击内容不同,并不是读写内存方式、注入内存方式、外挂核心核心代码载体差异的不同)

评分标准:满分10分

  1. flag(2分)    成功执行hack功能,给出外挂执行成功的flag。
  2. 代码(3分)    与hack.exe外挂功能相同,但实现原理与hack.exe不同的程序源码,仅需要提供可编译的工程完整代码,不需要提供已经编译出来的bin文件。
  3. 文档(4分)    详细描述解题过程,如涉及编写程序,必须提供源代码。
  4. 时间(1分)    正确提交flag、代码、文档的顺序,第1名计1分,后每1名-0.05分。

很明显,今年是写挂了。本fw通了两个宵只能很勉强的做出几个简单的功能。羡慕带哥直接dump SDK两小时生成带几十个功能的盖。(师傅们tql

hack.exe

加载程序进IDA,main函数开头解密字符串,试图打开hack.dat文件,因为dat文件不存在,所以hack.exe退出

我随便创了一个文件然后往里面写了一些东西,然后hack.exe就可以往下运行读取其中的内容然后进decode解密,decode函数里面是SSE优化后的解密算法,其中有两个分支,若长度大于0x40会走SSE优化的算法,若小于则走下面的分支,但本质的解密算法是一样的。具体算法后面给出分析

这一部分是计算shellcode的长度,shellcode会用于dll的加载

再下面v9就是decode函数对hack.dat解密出来的内容,通过取值取出ProcName,结合上下文可以知道这个ProcName就是游戏的进程名,在下面的 CreateToolhelp32Snapshot + Process32First 进程枚举中搜索游戏进程

再往下走下面两个循环分别是解密出flag和check flag

如果check成功则解密出 flag:%s 字符串并调用printf函数输出flag

下面会解密出一个dll并申请空间用于后续的注入

起动调程序,通过setIP手动的让程序运行decode函数解密DLL,然后通过idc脚本对其dump确定是一个dll文件,附dump.idc

#include <idc.idc>
#define PT_LOAD              1
#define PT_DYNAMIC           2
static main(void)
{
        auto ImageBase,StartImg,EndImg;
        auto i,dumpfile;
        StartImg=0x19B11F51440;
        EndImg=0x19B11F51440 + 0xFA00;
        if(dumpfile = fopen("D:\\DumpFile","wb"))
        {

            dump(dumpfile,StartImg,EndImg);
            fclose(dumpfile);
        }

}
static dump(dumpfile,startimg,endimg) 
{
        auto i;
        auto size;
        size = endimg-startimg;
        for ( i=0; i < size; i=i+1 ) 
        {
                fputc(Byte(startimg+i),dumpfile);
        }
}

利用注入器将DLL注入到游戏中,弹出提示,测试发现带有右键自瞄的外挂功能

回过头来继续看hack.exe。后面一大段都是解密出各种函数(这里就不一一截图了)

最后将dll,shellcode写入到游戏进程空间并起远线程调用shellcode加载dll

内容分析完了,用x64dbg对decode函数动态调试,获得解密逻辑,通过flag反推出正确hack.dat文件  附exp.py
string = "2RSRhrofoWtLeLrJCSlTireznrtx.oeLxuehyyAwbpCOZq0tsS7MZyVdOUoE8\x00\x00\x00"
# code = [
#     0x32,0x52,0x53,0x52,0x68,0x72,0x6F,0x66,0x6F,0x57,0x74,0x4C,0x65,0x4C,0x72,0x4A
#     ,0x43 ,0x53 ,0x6C ,0x54 ,0x69 ,0x72 ,0x65 ,0x7A ,0x6E ,0x72 ,0x74 ,0x78 ,0x2E ,0x6F ,0x65 ,0x4C  
#     ,0x78 ,0x75 ,0x65 ,0x68 ,0x79 ,0x79 ,0x41 ,0x77 ,0x62 ,0x70 ,0x43 ,0x4F ,0x5A ,0x71 ,0x30 ,0x74  
#     ,0x73 ,0x53 ,0x37 ,0x4D ,0x5A ,0x79 ,0x56 ,0x64 ,0x4F ,0x55 ,0x6F ,0x45 ,0x38 ,0x0, 0x0, 0x0]
code = [ord(x) for x in string]

i = 0;
j = 0;
k = 0;
for i in range(4):
    for j in range(16):
        code[k] += 0x13;
        code[k] = code[k] & 0xFF
        code[k] ^= 0x3F;
        k += 1

for i in range(len(code)):
    print(hex(code[i]), end=" ")

with open("hack.dat", 'wb') as fp:
    fp.write(bytes(code))

可以看到外挂成功启动并输出flag

Flag: 2RSRhrofoWtLeLrJCSlTireznrtx.oeLxuehyyAwbpCOZq0tsS7MZyVdOUoE8

DumpFile.dll

直接看这个dll发现很乱,来回看了一下发现是封装了一个hook引擎进去。外挂注入后可以实现右键自瞄的功能,同时只有敌人离自己在一定范围内才会触发自瞄。因为是按键自瞄,直接想到GetAsyncKeyState函数。IDA看dll的导入表果然看到交叉引用,只有sub_180005050一个函数调用过该函数,确定此处为作弊功能

发现浮点数写操作,0x398很像是一个偏移,附加上去跟了一下

动调跟了发现这两个值分别是角色的上下摇摆角和左右偏移角。向上回溯寻找v16的来源定位到这个地方

获得计算公式,发现此处的基址是出于作弊模块空间的,回到初始化函数找到原基址,输入CE

以此为入手点,从头看这个作弊函数。这个大循环就是自瞄了

GetAimTarget函数会尝试匹配符合条件的自瞄目标,匹配成功则返回目标对象,否则返回-1,其也是通过枚举角色结构列表来实现的

然后会获取每个角色的名字进行匹配

会在CampName函数中对名字进行字符串匹配

下面是距离计算逻辑

通过屏幕中心点与敌人的屏幕坐标 距离 来匹配距离准星 最近的敌人,若能匹配到,敌人对象就会通过v3返回。至此GetAimTarget函数分析完毕。

通过GetAimTarget取到的敌人对象,则通过内存对其取坐标,易分析得0x164,0x168,0x16C分别为对象的X,Y,Z坐标

最后会对坐标做角度变换和归一化处理,最终计算得到上下摇摆角和左右偏移角填入游戏数据

至此外挂功能函数分析完毕

外挂攻击的数据(实现自瞄的方式)

[[[[[ShooterClient.exe + 2F71060 + 160] + 38] + 0] + 30] + 398]    // 上下摇摆角
[[[[[ShooterClient.exe + 2F71060 + 160] + 38] + 0] + 30] + 39C]    // 左右偏移角

外挂实现

写了两个外挂,编译环境均为  visual studio 2019 x64 release

Cheat1

纯外部跨进程的通过修改镜头角度的实现右键自瞄

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <cstring>
#include "Offset.hpp"
#include "Myd3d.hpp"
#include "GameControler.hpp"
// #include "GameControlerByDriver.hpp"
using namespace std;

CGameControler GameControler("ShooterClient.exe");
D3DXVECTOR3 local_pos, target_pos;

string GetName(DWORD64 Entity)
{
    DWORD64 Index = GameControler.read<DWORD>((Entity + 0x18));
    DWORD64 Base = GameControler.read<DWORD64>((DWORD64)GameControler.GetGameBase() + offset_Name);
    DWORD64 tmp1 = GameControler.read<DWORD64>(Base + 8 * ((Index) / 0x4000));

    DWORD64 tmp2 = GameControler.read<DWORD64>(tmp1 + 8 * ((Index) % 0x4000));
    return GameControler.ReadString((LPCVOID)(tmp2 + 0xC), 64);
}

int Cheat()
{
    DWORD64 Uworld = GameControler.read<DWORD64>((DWORD64)GameControler.GetGameBase() + offsets_UWorld);
    // printf("Uworld:%llx\n", Uworld);
    DWORD64 ULevel = GameControler.read<DWORD64>(DWORD64(Uworld + offsets_Ulevel));
    DWORD64 AActor = GameControler.read<DWORD64>(DWORD64(ULevel + offsets_Actor));
    // printf("AActor:%p\n", AActor);
    DWORD num = GameControler.read<DWORD>(DWORD64(ULevel + offsets_Actor) + 8);

    DWORD64 GameInstance = GameControler.read<DWORD64>(DWORD64(Uworld + offsets_GameInstance));
    DWORD64 LocalPlayer = GameControler.read<DWORD64>( GameControler.read<DWORD64>(DWORD64(GameInstance + offsets_LocalPlayer)) );
    DWORD64 PlayerController = GameControler.read<DWORD64>(DWORD64(LocalPlayer + offsets_PlayerController));
    DWORD64 LocalPawn = GameControler.read<DWORD64>(DWORD64(PlayerController + offsets_LocalPawn));
    // printf("LocalPawn:%p\n", LocalPawn);
    // printf("My HP:%f\n\n", GameControler.read<float>((DWORD64)(LocalPawn + offsets_Health)));
    DWORD64 playobject = GameControler.read<DWORD64>(DWORD64(LocalPawn + offsets_PlayObject));
    float MyX = GameControler.read<float>(DWORD64(playobject + offsets_TargetX));
    float MyY = GameControler.read<float>(DWORD64(playobject + offsets_TargetY));
    float MyZ = GameControler.read<float>(DWORD64(playobject + offsets_TargetZ));
    // printf(" Local : X: %f \t Y : %f \t Z : %f\n", MyX, MyY, MyZ);
    local_pos = GameControler.read<D3DXVECTOR3>(playobject + offsets_TargetX);

    DWORD64* EntityList = (DWORD64*)malloc((num + 1) * sizeof(DWORD64*));
    GameControler.Read((LPCVOID)AActor, EntityList, num * sizeof(DWORD64*));
    for (int i = 0; i < num; ++i) {
        DWORD64 Entity = EntityList[i];
        if (Entity == LocalPawn)    continue;
        if (!Entity)                continue;

        float HP = GameControler.read<float>((DWORD64)(Entity + offsets_Health));
        if (HP <= 0.f)               continue;

        string sName = GetName(Entity);
        if (strcmp(sName.c_str(), "BotPawn_C"))   continue;

        DWORD64 playobject = GameControler.read<DWORD64>(DWORD64(Entity + offsets_PlayObject));
        float targetX = GameControler.read<float>(DWORD64(playobject + offsets_TargetX));
        float targetY = GameControler.read<float>(DWORD64(playobject + offsets_TargetY));
        float targetZ = GameControler.read<float>(DWORD64(playobject + offsets_TargetZ));

        // printf(" target[%d] : %s : X: %f \t Y : %f \t Z : %f\n", i, sName.c_str(),  targetX, targetY, targetZ);
        target_pos = GameControler.read<D3DXVECTOR3>(playobject + offsets_TargetX);
    }
    free(EntityList);
    // printf("\n");

    if (GetAsyncKeyState(2) != 0) {

        D3DXVECTOR3 angle = { 0, 0, 0 };
        float diff_x = target_pos.x - local_pos.x;                   // X差
        float diff_y = target_pos.y - local_pos.y;                   // Y差

        angle.x = atan2f(target_pos.z - local_pos.z, sqrtf((float)(diff_x * diff_x) + (float)(diff_y * diff_y)) ) * 57.295784;
        angle.y = (float)((float)(atan2f(diff_y, diff_x) * 360.0) * 0.25) / 1.5707963;
        if (angle.x < 0.0)
            angle.x = angle.x + 360.0;
        if (angle.y < 0.0)
            angle.y = angle.y + 360.0;
        GameControler.write<D3DXVECTOR3>(PlayerController + offsets_Pitch, angle);
        // printf("Write X : %f \t Y : %f\n", angle.x, angle.y);

    }
}

int main() {
    printf("Strat Cheating......\n");
    while (1) {
        Cheat();
        // Sleep(500);
    }
}
Cheat2

思路:因为要实现自瞄,而镜头又不知道还有什么别的攻击方式,因此换了一个思路,把子弹出发的坐标改到敌人的坐标上,这样可以开枪直接就可以打死敌人 并且可以无视任何建筑物。我从子弹数量入手,找到游戏开枪的地方,开枪的地方一定会有一个子弹开始坐标,我们先用CE搜索子弹数量,并对其下访问断点

定位到了子弹数量减少处。因为要实现修改子弹发射点到敌人坐标处,我们需要找到子弹发射的地方,因此这里向上回溯

回溯了一层发现大量的call,我们在头部下断,粗粗看了一下这些call,发现在子弹减少call的上一个call里调用了rand函数。

因为弹道是具有随机性的, rand函数引起了我的注意,然后想起第一天在百度上搜到的关于这款游戏的开发文档的源码

上下看了一下发现其他部分也很像,认为这个call就是武器开火函数

[/img][img=300,0]

从call的头部下断 然后在游戏中开枪 再往下可以看到调用了函数取了一个坐标,我不知道这个坐标具体是个什么坐标 但是肯定是跟弹道有关的坐标

我通过在此处下断点,面对墙壁开枪,并修改了这些值,发现墙壁上没有出现弹孔,认为这些值就是控制子弹的起始坐标,而下面这个call就是碰撞call。如果能修改这个参数为敌人的坐标,应该就可以实现子弹全图自瞄。 我采用了外部注入shellcode hook此处,跳转到写入的自瞄shellcode上,完成了参数的修改。经测试,确实可以达到开枪后子弹能直接打中敌人并且无视建筑物的效果。 同时,在我添加bot后发现bot开枪也是经过的这个函数 所以我们hook这个函数 不仅可以让bot的子弹打不中我们 我们还可以打死这些bot (同时实现了无敌跟子弹穿墙追踪的效果)

下面贴一下shellcode

// AimBotShell
push rcx                                                | 保护寄存器 压栈
push rdx                                                   | 保护寄存器 压栈
mov rcx,6666666666666666                            | target_pos_addr
mov rdx,qword ptr ds:[rcx]                              | 读取xy 两个8字节 所以直接用rdx
mov qword ptr ss:[rsp+70],rdx                           | 更改0x60这个位置 因为前面保护寄存器压栈了0x10字节,所以这里是0x70 
mov edx,dword ptr ds:[rcx+8]                            | 读取z坐标 4字节 所以是edx
mov dword ptr ss:[rsp+78],edx                           |
pop rdx                                                 | 恢复寄存器
pop rcx                                                 | 恢复寄存器 先进后出
mulss xmm8,xmm0                                     | 执行被破坏的原代码
mulss xmm7,xmm0                                     |
addss xmm8,dword ptr ss:[rsp+60]                        |
push 66666666                                       |跳回原处
mov dword ptr ss:[rsp+4],6666                           |
ret                                             |
// JmpShell 为了不影响寄存器 我们用栈做跳板 这样还能支持跨4gb跳转
push    66666666h
mov     dword ptr [rsp+4], 6666h
retn

待外挂启动后,会先将shellcode注入游戏空间,并跨进程的不断枚举游戏对象列表,发现存活的敌人便将其坐标写入自瞄坐标内存上,shellcode会在每次开枪的时候将自瞄坐标内存上的值替换到碰撞call的参数上,以此就可以实现站着不动开枪杀死全图敌人且无敌的效果。

// Cheat2.cpp
include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <cstring>
#include "Offset.hpp"
#include "Myd3d.hpp"
#include "GameControler.hpp"
// #include "GameControlerByDriver.hpp"
using namespace std;

CGameControler GameControler("ShooterClient.exe");

D3DXVECTOR3 target_pos;
DWORD64 target_pos_addr, payload_addr;
BYTE AimBotShell[] = "\x51\x52\x48\xB9\x66\x66\x66\x66\x66\x66\x66\x66\x48\x8B\x11\x48\x89\x54\x24\x70\x8B\x51\x08\x89\x54\x24\x78\x5A\x59\xF3\x44\x0F\x59\xC0\xF3\x0F\x59\xF8\xF3\x44\x0F\x58\x44\x24\x60\x68\x66\x66\x66\x66\xC7\x44\x24\x04\x66\x66\x00\x00\xC3";
/* AimBotShell
push rcx | 保护寄存器 压栈
push rdx | 保护寄存器 压栈
mov rcx, 6666666666666666 | target_pos_addr
mov rdx, qword ptr ds : [rcx] | 读取xy 两个8字节 所以直接用rdx
mov qword ptr ss : [rsp + 70] , rdx | 更改0x60这个位置 因为前面保护寄存器压栈了0x10字节,所以这里是0x70
mov edx, dword ptr ds : [rcx + 8] | 读取z坐标 4字节 所以是edx
mov dword ptr ss : [rsp + 78] , edx |
pop rdx | 恢复寄存器
pop rcx | 恢复寄存器 先进后出
mulss xmm8, xmm0 | 执行被破坏的原代码
mulss xmm7, xmm0 |
addss xmm8, dword ptr ss : [rsp + 60] |
push 66666666 | 跳回原处
mov dword ptr ss : [rsp + 4] , 6666 |
ret |
*/

BYTE JmpShell[] = "\x68\x66\x66\x66\x66\xC7\x44\x24\x04\x66\x66\x00\x00\xC3";
/* JmpShell
push    66666666h
mov     dword ptr [rsp+4], 6666h
retn
*/

string GetName(DWORD64 Entity)
{
    DWORD64 Index = GameControler.read<DWORD>((Entity + 0x18));
    DWORD64 Base = GameControler.read<DWORD64>((DWORD64)GameControler.GetGameBase() + offset_Name);
    DWORD64 tmp1 = GameControler.read<DWORD64>(Base + 8 * ((Index) / 0x4000));
    DWORD64 tmp2 = GameControler.read<DWORD64>(tmp1 + 8 * ((Index) % 0x4000));
    return GameControler.ReadString((LPCVOID)(tmp2 + 0xC), 64);
}

int Cheat()
{

    DWORD64 Uworld = GameControler.read<DWORD64>((DWORD64)GameControler.GetGameBase() + offsets_UWorld);
    // printf("Uworld:%llx\n", Uworld);
    DWORD64 ULevel = GameControler.read<DWORD64>(DWORD64(Uworld + offsets_Ulevel));
    DWORD64 AActor = GameControler.read<DWORD64>(DWORD64(ULevel + offsets_Actor));
    DWORD num = GameControler.read<DWORD>(DWORD64(ULevel + offsets_Actor) + 8);     // 取对象数量

    DWORD64 GameInstance = GameControler.read<DWORD64>(DWORD64(Uworld + offsets_GameInstance));
    DWORD64 LocalPlayer = GameControler.read<DWORD64>( GameControler.read<DWORD64>(DWORD64(GameInstance + offsets_LocalPlayer)) );
    DWORD64 PlayerController = GameControler.read<DWORD64>(DWORD64(LocalPlayer + offsets_PlayerController));
    DWORD64 LocalPawn = GameControler.read<DWORD64>(DWORD64(PlayerController + offsets_LocalPawn));
    // printf("My HP:%f\n\n", GameControler.read<float>((DWORD64)(LocalPawn + offsets_Health)));

    DWORD64* EntityList = (DWORD64*)malloc((num + 1) * sizeof(DWORD64*));
    GameControler.Read((LPCVOID)AActor, EntityList, num * sizeof(DWORD64*));
    for (int i = 0; i < num; ++i) {
        DWORD64 Entity = EntityList[i];
        if (Entity == LocalPawn)    continue;
        if (!Entity)                continue;

        float HP = GameControler.read<float>((DWORD64)(Entity + offsets_Health));
        if (HP <= 0.f)               continue;

        string sName = GetName(Entity);
        if (strcmp(sName.c_str(), "BotPawn_C"))   continue;

        DWORD64 playobject = GameControler.read<DWORD64>(DWORD64(Entity + offsets_PlayObject));
        float targetX = GameControler.read<float>(DWORD64(playobject + offsets_TargetX));
        float targetY = GameControler.read<float>(DWORD64(playobject + offsets_TargetY));
        float targetZ = GameControler.read<float>(DWORD64(playobject + offsets_TargetZ));

        // printf(" target[%d] : %s : X: %f \t Y : %f \t Z : %f\n", i, sName.c_str(),  targetX, targetY, targetZ);
        target_pos = GameControler.read<D3DXVECTOR3>(playobject + offsets_TargetX);
        GameControler.Write((LPVOID)target_pos_addr, target_pos, 12);  // 写入敌人坐标至自瞄坐标
    }
    free(EntityList);
}

int main() {

    printf("Strat Cheating......\n");
    target_pos_addr = (DWORD64)VirtualAllocEx(GameControler.GetHandle(), NULL, 4096, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
    if (target_pos_addr == NULL) {
        printf("VirtualAllocEx target_pos_addr Error!  Error Code:%d\n", GetLastError());
        exit(0);
    }
    payload_addr = (DWORD64)VirtualAllocEx(GameControler.GetHandle(), NULL, 4096, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
    if (payload_addr == NULL) {
        printf("VirtualAllocEx payload_addr Error!  Error Code:%d\n", GetLastError());
        exit(0);
    }

    DWORD64 HookAddress = (DWORD64)GameControler.GetGameBase() + 0x51C48C;
    DWORD64 ReturnAddress = (DWORD64)GameControler.GetGameBase() + 0x51C49C;
    *(DWORD64*)(AimBotShell + 0x4) = target_pos_addr;
    *(DWORD*)(AimBotShell + 0x2E) = *(DWORD32*)(&ReturnAddress);
    *(DWORD*)(AimBotShell + 0x36) = *(DWORD32*)((DWORD64)(&ReturnAddress) + 4);

    *(DWORD*)(JmpShell + 0x1) = *(DWORD32*)(&payload_addr);                 
    *(DWORD*)(JmpShell + 0x9) = *(DWORD32*)((DWORD64)(&payload_addr) + 4); 

    GameControler.Write((LPVOID)payload_addr, AimBotShell, sizeof(AimBotShell) - 1);

    DWORD OldProtect{ 0 };
    BOOL nStatus = TRUE;
    nStatus = VirtualProtectEx(GameControler.GetHandle(), (LPVOID)HookAddress, 0x1000, PAGE_EXECUTE_READWRITE, &OldProtect);
    GameControler.Write((LPVOID)HookAddress, JmpShell, sizeof(JmpShell) - 1);
    nStatus = VirtualProtectEx(GameControler.GetHandle(), (LPVOID)HookAddress, 0x1000, OldProtect, &OldProtect);

    printf("target_pos_addr:%llx \t payload_addr:%llx\n", target_pos_addr, payload_addr);
    if (nStatus == TRUE)
        printf("Cheat Init Success!\n");
    else {
        printf("Cheat Init Fail!\t Error Code:%d\n", GetLastError());
        exit(-1);
    }
    while (1) {
        Cheat();
        // Sleep(500);
    }
}

至此,外挂实现完毕

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册[Register]

x

免费评分

参与人数 42吾爱币 +41 热心值 +37 收起 理由
zhenkaixin852 + 1 + 1 谢谢@Thanks!
ACKI + 1 我很赞同!
sync700 + 1 + 1 膜拜大佬
100斤d胖子 + 1 + 1 太强了
小脚jio + 1 + 1 我很赞同!
QIQI奇奇 + 1 用心讨论,共获提升!
52_pojie_52 + 1 + 1 用心讨论,共获提升!
c12111 + 1 + 1 膜拜膜拜
qdam + 1 我很赞同!
exluku + 1 + 1 热心回复!
tandz + 1 + 1 我很赞同!
Qzone49 + 1 + 1 热心回复!
shiina0831 + 1 + 1 谢谢@Thanks!
JimOvO + 1 谢谢@Thanks!
404undefined + 1 + 1 鼓励转贴优秀软件安全工具和文档!
m1n9yu3 + 1 + 1 我很赞同!
daisukikotori + 1 我很赞同!
xiaoyu0814 + 1 用心讨论,共获提升!
rekaytang + 1 + 1 我很赞同!
Marsable + 1 + 1 我很赞同!
唐小样儿 + 1 + 1 我很赞同!
Ah0NoR + 1 + 1 热心回复!
易请惜 + 1 + 1 用心讨论,共获提升!
linden007x + 1 用心讨论,共获提升!
pdcba + 1 + 1 热心回复!
fanny188 + 2 热心回复!
gunxsword + 1 + 1 这就是传说中的子弹追踪吧
System + 2 + 1 我很赞同!
aaa661179 + 1 + 1 热心回复!
夜雨微微凉 + 1 我很赞同!
azcolf + 1 + 1 热心回复!
奶味小可爱 + 1 + 1 热心回复!
chuxia12 + 1 我很赞同!
一叶丶 + 1 + 1 太强了
天空宫阙 + 2 + 1 牛逼
雷志勇 + 1 + 1 感谢您的宝贵建议,我们会努力争取做得更好!
zsky + 2 + 1 谢谢@Thanks!
独行风云 + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
fengbolee + 1 + 1 用心讨论,共获提升!
蒋大帅 + 1 + 1 膜拜啊!
Pau250 + 2 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
e.wgs + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

Hmily 发表于 2021-4-12 12:17
@Qfrost 图片直接用discuz的方式插入,你看这里学习下https://www.52pojie.cn/misc.php? ... 29&messageid=36 ,然后重新编辑下帖子吧。
Hmily 发表于 2021-4-12 14:23
Qfrost 发表于 2021-4-12 14:21
我编辑页面显示都是正常的,麻烦站务帮我检查一下吧

md会禁止使用discuz的img标签功能,你预览是可以,发出来就不行了,主要原因还是你插入的方法不对,不需要复杂到复制图片地址,只需要点击图片就可以了,你看下我给你发的帮助链接,看下操作。
Airey 发表于 2021-4-12 12:37
ZJevon 发表于 2021-4-12 13:34
大师傅tql
 楼主| Qfrost 发表于 2021-4-12 14:14
Hmily 发表于 2021-4-12 12:17
@Qfrost 图片直接用discuz的方式插入,你看这里学习下https://www.52pojie.cn/misc.php?mod=faq&action=faq ...

这个问题我早上调了好久,md换dz插入,现在我编辑页面显示是正常的,但是帖子里就是这样,我也不知道为什么

点评

不要用img标签,你看上面我发的帮助,直接点击图片就自动插入了,不用引用地址,这些地址都是临时的。  详情 回复 发表于 2021-4-12 14:21
jjyywg 发表于 2021-4-12 14:16
膜拜膜拜!!!!
Hmily 发表于 2021-4-12 14:21
Qfrost 发表于 2021-4-12 14:14
这个问题我早上调了好久,md换dz插入,现在我编辑页面显示是正常的,但是帖子里就是这样,我也不知道为什 ...

不要用img标签,你看上面我发的帮助,直接点击图片就自动插入了,不用引用地址,这些地址都是临时的。
 楼主| Qfrost 发表于 2021-4-12 14:21
Hmily 发表于 2021-4-12 12:17
@Qfrost 图片直接用discuz的方式插入,你看这里学习下https://www.52pojie.cn/misc.php?mod=faq&action=faq ...


我编辑页面显示都是正常的,麻烦站务帮我检查一下吧

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册[Register]

x

点评

md会禁止使用discuz的img标签功能,你预览是可以,发出来就不行了,主要原因还是你插入的方法不对,不需要复杂到复制图片地址,只需要点击图片就可以了,你看下我给你发的帮助链接,看下操作。  详情 回复 发表于 2021-4-12 14:23
Hmily 发表于 2021-4-12 14:36
@Qfrost 看起来还有一个图丢了?
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-11-15 12:11

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表