新版某数字壳脱壳,过frida检测,及重打包

news2024/11/13 20:48:15

目录

脱壳

寻找特征& frida hook

过frida检测

修复dex

重打包

修改smail

去签名校验

正文

大家好,我是小生,这次的app是一个国内某计划app, 功能相当全,界面也很美观,很实用,这个app我很欣赏。总共花了有三天晚上加一个白天,抓包分析,脱壳,过检测,手撕smail, 调试等, 做开发好久了,逆向有段时间没有接触了,很生疏了

就是会员太贵了,终身会员300多嘞!

【为该公司的权益考虑,不提供成品,也不提供app相关 信息】
(现在大大小小的app全都加壳,甚至一些颜色灰产的也加国内的这些壳!!! 动不动就抽取,dex2c,都不能愉快的好好玩耍了)​

脱壳

还有asserts目录下的 libjiagu.so 就知道是数字壳无疑了! 

apktool解包,发现6月份的新版数字加固

脱壳用的fart改的脱壳机,详细过程就不赘述了

 

总共脱下来21个dex,一个个先脱进jadx中看看是否都是有用的,发现有两个全是壳相关的,剩下了19个

发现脱下来还是相对较完整的,里面也有损坏的部分,但影响不太大,

寻找特征& frida hook

因为这次我需要的是里面的vip功能,按照惯例先搜isVip等字样
发现搜出来很多结果,不影响,排除掉本app的广告sdk和依赖的库,一个个看,看和用户相关的,发现两个类都是相关的
直接写hook,

 只截取部分,

然后 frida启动!
我是先attach启动的,发现会闪退 

换成去掉部分特征的strongr frida发现还是如此,
1.我又尝试了换端口,span启动,
2.hook libc.so中的 strstr,strcmp来去掉内存里的frida,gmain,gdbus等字样
3.hook 重定向/proc/xxx/maps
4.hook libc.so的exit
5.hook android.os.Process的killProcess
再配合上常用的几个过检测脚本还是一样闪退,感觉事情不简单了

过frida检测

提前说一嘴,这个frida检测不是在壳里,是在app的so里,还有hook这个业务代码要延迟一段时间执行,不然classloader还没有加载相关类。

既然不是在java层,那就是在native层检测的了,通常是hook android_dlopen_ext,观察加载到哪个so的时候退出就可以定位到了,

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

function hook_dlopenAndExt() {

    Interceptor.attach(Module.findExportByName(null"dlopen"), {

        onEnter: function (args) {

            var pathptr = args[0];

            if (pathptr !== undefined && pathptr != null) {

                var path = ptr(pathptr).readCString();

                //console.log("dlopen:", path);

                // if (path.indexOf("libart.so") >= 0) {

                //     // this.can_hook_libart = true;

                //     console.log("[dlopen:]", path);

                // }

                console.log("load " + path);

            }

        },

        onLeave: function (retval) {

            // if (this.can_hook_libart && !is_hook_libart) {

            //     dump_dex();

            //     is_hook_libart = true;

            // }

        }

    })

    Interceptor.attach(Module.findExportByName(null"android_dlopen_ext"), {

        onEnter: function (args) {

            var pathptr = args[0];

            if (pathptr !== undefined && pathptr != null) {

                var path = ptr(pathptr).readCString();

                //console.log("android_dlopen_ext:", path);

                // if (path.indexOf("libart.so") >= 0) {

                //     // this.can_hook_libart = true;

                //     console.log("[android_dlopen_ext:]", path);

                // }

                console.log("load " + path);

            }

        },

        onLeave: function (retval) {

            // if (this.can_hook_libart && !is_hook_libart) {

            //     dump_dex();

            //     is_hook_libart = true;

            // }

        }

    });

}

 

可以定位到是在libmxxxdesc.so中

然后hook pthread_create函数,尝试找到来自libmxxxdesc.so创建的检测线程

然后就一直卡在那了,一直也找不到来自该so的创建线程的调用,
下面的部分借鉴看雪的看雪bilibili frida过检测
把so放进ida中也没有发现有创建线程的导入符号 

尝试从更早的时机,通过hook dlsym函数来看是否有通过dlsym来获取pthread_create地址来进行调用

发现确实调用了创建线程的函数,只不过不是直接调用,而是采用通过dlsym获取地址再调用

 

图片描述

下面采用创建一个虚假的创建函数的地址返回,来欺骗目标so(还是来源于看雪bilibili frida过检测的思路和代码)

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

function create_fake_pthread_create() {

    const fake_pthread_create = Memory.alloc(4096)

    Memory.protect(fake_pthread_create, 4096, "rwx")

    Memory.patchCode(fake_pthread_create, 4096, code => {

        const cw = new Arm64Writer(code, { pc: ptr(fake_pthread_create) })

        cw.putRet()

    })

    return fake_pthread_create

}

  

function hook_dlsym() {

    var count = 0

    console.log("=== HOOKING dlsym ===")

    var interceptor = Interceptor.attach(Module.findExportByName(null"dlsym"),

        {

            onEnter: function (args) {

                const name = ptr(args[1]).readCString()

                console.log("[dlsym]", name)

                if (name == "pthread_create") {

                    count++

                }

            },

            onLeave: function(retval) {

                if (count == 1) {

                    retval.replace(fake_pthread_create)

                }

                else if (count == 2) {

                    retval.replace(fake_pthread_create)

                    // 完成2次替换, 停止hook dlsym

                    interceptor.detach()

                }

            }

        }

    )

    return Interceptor

}

  

function hook_dlopen() {

    var interceptor = Interceptor.attach(Module.findExportByName(null"android_dlopen_ext"),

        {

            onEnter: function (args) {

                var pathptr = args[0];

                if (pathptr !== undefined && pathptr != null) {

                    var path = ptr(pathptr).readCString();

                    console.log("[LOAD]", path)

                    if (path.indexOf("libmxxxxxec.so") > -1) {

                        hook_dlsym()

                    }

                }

            }

        }

    )

    return interceptor

}

  

// 创建虚假pthread_create

var fake_pthread_create = create_fake_pthread_create()

var dlopen_interceptor = hook_dlopen()

就过掉了检测

 

 

修复dex

通过hook关键的函数发现确实可以达到付费vip的效果,但是部分界面显示的vip样式还是有点问题,
我逐个把dex脱进jadx中,进行查看,去除掉没用的dex, 发现可以去除掉两个全是数字壳的特征dex,

1.然后使用MT管理器把这21个dex替换了原来的dex
2.然后把asserts文件夹中的libjiagu.so 那四个数字壳的so文件删掉

3.然后把AndroidMinfest.xml中原来的com.stub.StubApp为程序真正的入口com.xxxxxx
这个app是真的大,光androidMinfest文件就干出去将近5000行!!(后面改smail的时候很痛苦)

重打包

 

然后重打包编译,进行jarsinger签名,一气呵成,安装,闪退! 漂亮!

我一开始以为是不是有签名验证啊,我就再jadx中进行搜素packagemanager相关的,但都关系不太大,最后发现是脱壳还有数字的残留特征,
就是下面这种效果,1000多条!

 

invoke-static {p0, p1, p2}, Lcom/stub/StubApp;->interface24(Landroid/app/Activity;[Ljava/lang/String;I)V

可以用正则的方式匹配替换掉,这里很麻烦,我替换了整整有半个多小时,各种各样的,真恶心!
这里我是使用 一键正则 工具走捷径了(尽管这样,也很慢)
下面这两个可以通用替换掉一些,但还是会有很多很多很多漏网之鱼

invoke-static/range \{.* \.\. .*\}, Lcom/stub/StubApp;->getOrigApplicationContext\(Landroid/content/Context;\)Landroid/content/Context;\s+move-result-object .*
invoke-super {p0, p1}, Landroid/app/Activity;->onCreate(Landroid/os/Bundle;)V

其实这个app算我运气好,onCreate函数没有被抽取掉,很赏脸了!

再都完成替换之后,确认没有stubapp, stub/stub等数字壳特征之后,再进行重打包,签名,发现可以打开了,我测试了一下,里面有两个子页面有点问题,打开会闪退,不过我会用到的页面都正常,(这个app大大小小加起来有62个页面,那两个无所谓)

修改smail

首先声明一下,我不会smail(以下纯现学现用,所以看着像屎一样很正常)
这一步就没有什么技术含量了,(对于我这种小卡拉米以及 这种简单的app而言),主要是耐心和细心,
这里我是采用mt管理器来进行编辑的,不得不说,确实很方便,但是改smail也很麻烦,要操作寄存器,改完还不知道,只能重打包后安装才能验证出来,一不小心改错就会闪退,前文说到有两个相关的类,一个有get set方法,很好处理,get的话直接

const/4 v1, 0x1
然后return 或者赋值都可以,

1

2

3

4

5

6

7

8

9

10

11

12

13

14

public class Uxxxxfo implements Serializable {

    public List<AdX> adxList;

    public boolean axxxxeVip;

    public String alxxxcon;

    public String axxxge;

    public CheckFreeVipInfo cxxxnfo;

    public boolean evxxp;

    public int exxxxay;

    public String id;

    public boolean isPoxxxp;

    public boolean isxxxit;

    public boolean isVip;

......

......

还有一个bean全是public字段,没有get set方法,而且引用的地方相当多,我没有办法在构造函数中进行赋值,因为后续会被覆盖掉,这里我有想到用抓包改包的方式,我在有root和xposed的测试机上试验过,没问题,但我想在没有root和xposed的环境使用,这种方案显然不可行
我只能在每一个用到的地方都进行修改,比如

Lcom/dxxxx/lxxxxon/model/Uxxxxfo;->id:Ljava/lang/String;

    check-cast v2, Ljava/lang/CharSequence;

    invoke-static {v2}, Landroid/text/TextUtils;->isEmpty(Ljava/lang/CharSequence;)Z

    move-result v2

    const-string v8, "mContext"

    if-nez v2, :cond_218

    iget-boolean v2, v1, Lcom/xxx/xxxxx/model/Usxxxxxfo;->vstate:Z

    if-nez v2, :cond_7f

    goto/16 :goto_218
    
    我要保证vstate一直为true
    我改成下面的
    const/4 v2, 0x1  # 将常量 1(true)存储到寄存器 v2
    iput-boolean v2, v1, Lcom/xxx/xxxxx/model/Usxxxxxfo;->vstate:Z
    iget-boolean v2, v1, Lcom/xxx/xxxxx/model/Usxxxxxfo;->vstate:Z
    if-nez v2, :cond_7f

放一张成品吧

 

后记

之前一直采用的charles+postern方式抓包,用花哥的话说,走socket,靠近底层,能获取更多的上层流量

现在我改成了 Reqable小黄鸟来抓包,头一次用,挺方便的,也是要root,这个没得跑,
关于证书安装的问题,安卓7以后要手动remount,把证书移动到/system/etc/security/cacerts目录下
我试了好几次,小黄鸟都识别不到证书已安装,尽管64xxxk.0已经在系统证书目录中,后来我尝试移除charles证书,试了两次,重启过后可以了! 至于没有网络或者其他的问题导致无法安装证书,我的博客里有记载。

总结

敢于尝试,就有成功的可能

代码

文中所用到的部分过检测代码

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

194

195

196

197

198

199

200

201

202

203

204

205

206

207

208

209

210

211

212

213

214

215

216

217

218

219

220

221

222

223

224

225

226

227

228

229

230

231

232

233

234

235

236

237

238

239

240

241

242

243

244

245

246

247

248

249

250

251

252

253

254

255

256

257

258

259

260

261

262

263

264

265

266

267

268

269

270

271

272

273

274

275

276

277

278

279

280

281

282

283

284

285

286

287

288

289

290

291

292

293

294

295

296

297

298

299

300

301

302

303

304

305

306

307

308

309

310

311

312

313

314

315

316

317

318

319

320

321

322

323

324

325

326

327

328

329

330

331

332

333

334

335

336

337

338

339

340

341

342

343

344

function loadGson() {

    Java.openClassFile("/data/local/tmp/xiaosheng-dex-tool.dex").load();

    var js = Java.use("com.xiaosheng.tool.json.Gson");

    var gson = js.$new();

    return gson;

}

function hook_dlopen_ext() {

    Interceptor.attach(Module.findExportByName(null"android_dlopen_ext"),

        {

            onEnter: function (args) {

                var pathptr = args[0];

                if (pathptr !== undefined && pathptr != null) {

                    var path = ptr(pathptr).readCString();

                    console.log("load " + path);

                }

            }

        }

    );

}

function hook_dlopenAndExt() {

    Interceptor.attach(Module.findExportByName(null"dlopen"), {

        onEnter: function (args) {

            var pathptr = args[0];

            if (pathptr !== undefined && pathptr != null) {

                var path = ptr(pathptr).readCString();

                //console.log("dlopen:", path);

                // if (path.indexOf("libart.so") >= 0) {

                //     // this.can_hook_libart = true;

                //     console.log("[dlopen:]", path);

                // }

                console.log("load " + path);

            }

        },

        onLeave: function (retval) {

            // if (this.can_hook_libart && !is_hook_libart) {

            //     dump_dex();

            //     is_hook_libart = true;

            // }

        }

    })

    Interceptor.attach(Module.findExportByName(null"android_dlopen_ext"), {

        onEnter: function (args) {

            var pathptr = args[0];

            if (pathptr !== undefined && pathptr != null) {

                var path = ptr(pathptr).readCString();

                //console.log("android_dlopen_ext:", path);

                // if (path.indexOf("libart.so") >= 0) {

                //     // this.can_hook_libart = true;

                //     console.log("[android_dlopen_ext:]", path);

                // }

                console.log("load " + path);

            }

        },

        onLeave: function (retval) {

            // if (this.can_hook_libart && !is_hook_libart) {

            //     dump_dex();

            //     is_hook_libart = true;

            // }

        }

    });

}

function hook_open() {

    var pth = Module.findExportByName(null"open");

    Interceptor.attach(ptr(pth), {

        onEnter: function (args) {

            this.filename = args[0];

            console.log(""this.filename.readCString())

            if (this.filename.readCString().indexOf(".so") != -1) {

                args[0] = ptr(0)

            }

        }, onLeave: function (retval) {

            return retval;

        }

    })

}

function hookProcess() {

    var process = Java.use("android.os.Process");

    process.killProcess.implementation = function (pid) {

        console.log("kill process:" + pid)

    }

}

function hookExit() {

    var ByPassTracerPid = function () {

        var fgetsPtr = Module.findExportByName("libc.so""fgets");

        var fgets = new NativeFunction(fgetsPtr, 'pointer', ['pointer''int''pointer']);

        Interceptor.replace(fgetsPtr, new NativeCallback(function (buffer, size, fp) {

            var retval = fgets(buffer, size, fp);

            var bufstr = Memory.readUtf8String(buffer);

            if (bufstr.indexOf("TracerPid:") > -1) {

                Memory.writeUtf8String(buffer, "TracerPid:\t0");

                console.log("tracerpid replaced: " + Memory.readUtf8String(buffer));

            }

            return retval;

        }, 'pointer', ['pointer''int''pointer']));

    };

}

function hook_Pthreadfunc() {

    var pthread_creat_addr = Module.findExportByName("libc.so""pthread_create")

    Interceptor.attach(pthread_creat_addr, {

        onEnter(args) {

            console.log("call pthread_create...")

            let func_addr = args[2]

            console.log("The thread function address is " + func_addr)

            try {

                console.log('pthread_create called from:\n'

                    + Thread.backtrace(this.context, Backtracer.ACCURATE)

                        .map(DebugSymbol.fromAddress)

                        .join('\n')

                    '\n');

            catch (e) {

            }

        }

    })

}

function hookBaseExit() {

    function main() {

        const openPtr = Module.getExportByName('libc.so''open');

        const open = new NativeFunction(openPtr, 'int', ['pointer''int']);

        var readPtr = Module.findExportByName("libc.so""read");

        var read = new NativeFunction(readPtr, 'int', ['int''pointer'"int"]);

        // var fakePath = "/sdcard/app/maps/maps";

        var fakePath = "/data/local/tmp/fakeMap";

        var file = new File(fakePath, "w");

        var buffer = Memory.alloc(512);

        Interceptor.replace(openPtr, new NativeCallback(function (pathnameptr, flag) {

            var pathname = Memory.readUtf8String(pathnameptr);

            var realFd = open(pathnameptr, flag);

            if (pathname.indexOf("maps") != 0) {

                while (parseInt(read(realFd, buffer, 512)) !== 0) {

                    var oneLine = Memory.readCString(buffer);

                    if (oneLine.indexOf("tmp") === -1) {

                        file.write(oneLine);

                    }

                }

                var filename = Memory.allocUtf8String(fakePath);

                return open(filename, flag);

            }

            var fd = open(pathnameptr, flag);

            return fd;

        }, 'int', ['pointer''int']));

    }

    setImmediate(main)

}

function replace_str() {

    var pt_strstr = Module.findExportByName("libc.so"'strstr');

    var pt_strcmp = Module.findExportByName("libc.so"'strcmp');

    Interceptor.attach(pt_strstr, {

        onEnter: function (args) {

            var str1 = args[0].readCString();

            var str2 = args[1].readCString();

            if (str2.indexOf("tmp") !== -1 ||

                str2.indexOf("frida") !== -1 ||

                str2.indexOf("gum-js-loop") !== -1 ||

                str2.indexOf("gmain") !== -1 ||

                str2.indexOf("gdbus") !== -1 ||

                str2.indexOf("pool-frida") !== -1 ||

                str2.indexOf("linjector") !== -1) {

                // console.log("strcmp-->", str1, str2);

                this.hook = true;

            }

        }, onLeave: function (retval) {

            if (this.hook) {

                retval.replace(0);

            }

        }

    });

    Interceptor.attach(pt_strcmp, {

        onEnter: function (args) {

            var str1 = args[0].readCString();

            var str2 = args[1].readCString();

            if (str2.indexOf("tmp") !== -1 ||

                str2.indexOf("frida") !== -1 ||

                str2.indexOf("gum-js-loop") !== -1 ||

                str2.indexOf("gmain") !== -1 ||

                str2.indexOf("gdbus") !== -1 ||

                str2.indexOf("pool-frida") !== -1 ||

                str2.indexOf("linjector") !== -1) {

                // console.log("strcmp-->", str1, str2);

                this.hook = true;

            }

        }, onLeave: function (retval) {

            if (this.hook) {

                retval.replace(0);

            }

        }

    })

}

// 定义一个函数anti_maps,用于阻止特定字符串的搜索匹配,避免检测到敏感内容如"Frida"或"REJECT"

function anti_maps() {

    // 查找libc.so库中strstr函数的地址,strstr用于查找字符串中首次出现指定字符序列的位置

    var pt_strstr = Module.findExportByName("libc.so"'strstr');

    // 查找libc.so库中strcmp函数的地址,strcmp用于比较两个字符串

    var pt_strcmp = Module.findExportByName("libc.so"'strcmp');

    // 使用Interceptor模块附加到strstr函数上,拦截并修改其行为

    Interceptor.attach(pt_strstr, {

        // 在strstr函数调用前执行的回调

        onEnter: function (args) {

            // 读取strstr的第一个参数(源字符串)和第二个参数(要查找的子字符串)

            var str1 = args[0].readCString();

            var str2 = args[1].readCString();

            // 检查子字符串是否包含"REJECT"或"frida",如果包含则设置hook标志为true

            if (str2.indexOf("REJECT") !== -1 || str2.indexOf("frida") !== -1) {

                this.hook = true;

            }

        },

        // 在strstr函数调用后执行的回调

        onLeave: function (retval) {

            // 如果之前设置了hook标志,则将strstr的结果替换为0(表示未找到),从而隐藏敏感信息

            if (this.hook) {

                retval.replace(0);

            }

        }

    });

    // 对strcmp函数做类似的处理,防止通过字符串比较检测敏感信息

    Interceptor.attach(pt_strcmp, {

        onEnter: function (args) {

            var str1 = args[0].readCString();

            var str2 = args[1].readCString();

            if (str2.indexOf("REJECT") !== -1 || str2.indexOf("frida") !== -1) {

                this.hook = true;

            }

        },

        onLeave: function (retval) {

            if (this.hook) {

                // strcmp返回值为0表示两个字符串相等,这里同样替换为0以避免匹配成功

                retval.replace(0);

            }

        }

    });

}

const STD_STRING_SIZE = 3 * Process.pointerSize;

class StdString {

    constructor() {

        this.handle = Memory.alloc(STD_STRING_SIZE);

    }

    dispose() {

        const [data, isTiny] = this._getData();

        if (!isTiny) {

            Java.api.$delete(data);

        }

    }

    disposeToString() {

        const result = this.toString();

        this.dispose();

        return result;

    }

    toString() {

        const [data] = this._getData();

        return data.readUtf8String();

    }

    _getData() {

        const str = this.handle;

        const isTiny = (str.readU8() & 1) === 0;

        const data = isTiny ? str.add(1) : str.add(2 * Process.pointerSize).readPointer();

        return [data, isTiny];

    }

}

function prettyMethod(method_id, withSignature) {

    const result = new StdString();

    Java.api['art::ArtMethod::PrettyMethod'](result, method_id, withSignature ? 1 : 0);

    return result.disposeToString();

}

function hook_libc_exit() {

    var exit = Module.findExportByName("libc.so""exit");

    console.log("native:" + exit);

    Interceptor.attach(exit, {

        onEnter: function (args) {

            try {

                console.log(Thread.backtrace(this.context, Backtracer.FUZZY).map(DebugSymbol.fromAddress).join("\n"));

            catch (e) {

                console.log(e)

            }

        },

        onLeave: function (retval) {

            //send("gifcore so result value: "+retval);

        }

    });

}

function anti_exit() {

    const exit_ptr = Module.findExportByName(null'_exit');

    // DMLog.i('anti_exit', "exit_ptr : " + exit_ptr);

    console.log("anti_kill, kill_ptr:" + exit_ptr)

    if (null == exit_ptr) {

        return;

    }

    Interceptor.replace(exit_ptr, new NativeCallback(function (code) {

        if (null == this) {

            return 0;

        }

        // var lr = FCCommon.getLR(this.context);

        // DMLog.i('exit debug', 'entry, lr: ' + lr);

        console.log("kill debug entry,lr")

        return 0;

    }, 'int', ['int''int']));

}

function anti_kill() {

    const kill_ptr = Module.findExportByName(null'kill');

    // DMLog.i('anti_kill', "kill_ptr : " + kill_ptr);

    console.log("anti_kill, kill_ptr:" + kill_ptr)

    if (null == kill_ptr) {

        return;

    }

    Interceptor.replace(kill_ptr, new NativeCallback(function (ptid, code) {

        if (null == this) {

            return 0;

        }

        // var lr = FCCommon.getLR(this.context);

        // DMLog.i('kill debug', 'entry, lr: ' + lr);

        console.log("kill debug entry,lr")

        // FCAnd.showNativeStacks(this.context);

        return 0;

    }, 'int', ['int''int']));

}

// FCCommon哪个库我引用一直有问题,就把那段代码注释掉了

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2101740.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【SQL】Delete使用

目录 语法 需求 示例 分析 代码 语法 DELETE删除表中所需内容 删除表中满足特点条件的行&#xff1a;DELETE FROM 表名 WHERE 条件; 删除表中所有行&#xff1a;DELETE FROM 表名; WHERE子句 WHERE子句用于指定从表中选取记录的条件。允许筛选数据&#xff0c;只返回满足…

【文献精读】基于驱动力表的无人车终端无约束预测纵向控制(TVT)

写在前面&#xff1a; &#x1f31f; 欢迎光临 清流君 的博客小天地&#xff0c;这里是我分享技术与心得的温馨角落。&#x1f4dd; 个人主页&#xff1a;清流君_CSDN博客&#xff0c;期待与您一同探索 移动机器人 领域的无限可能。 &#x1f50d; 本文系 清流君 原创之作&…

ElasticSearch学习笔记(六)自动补全、拼音分词器、RabbitMQ实现数据同步

文章目录 前言11 自动补全11.1 拼音分词器11.2 自定义分词器11.3 自动补全查询 12 数据同步12.1 实现方案12.1.1 同步调用12.1.2 异步通知12.1.3 监听binlog 12.2 异步通知实现数据同步12.2.1 声明交换机和队列12.2.2 发送MQ消息12.2.3 接收MQ消息并操作ES 前言 ElasticSearch…

数据结构————单向链表

头插&#xff1a; 尾插&#xff1a; 头删&#xff1a; 尾删&#xff1a;

一种常用嵌入式开发代码库

链接&#xff1a;https://gitee.com/zhangxinyuanqi/varch 使用开源协议&#xff1a;GPL-2.0 varch简介 varch&#xff08;we-architecture&#xff0c;意为我们的框架库&#xff09;是嵌入式C语言常用代码模块库&#xff0c;包含了嵌入式中常用的算法库, 数据结构&#xff…

JPA关联MyBatis

3.1 JPA 多表查询 多表查询在 Spring Data JPA 中有两种实现方式&#xff0c;第一种是创建一个结果集的接口来接受多表连接查询后的结果&#xff0c;第二种是利用 JPA 的关联映射来实现 3.1.1 数据库表及关系 CRM 数据库中除 sys_user(用户)表外&#xff0c;还包括sys_role(角…

触想内嵌式工业一体机应用于智能检票机改善旅游体验

一、行业发展背景 每年下半年&#xff0c;暑假、中秋、国庆总是接踵而至&#xff0c;随之而来的出游高峰一波接一波。凶猛需求之下&#xff0c;各地景区、游乐园客流压力加大&#xff0c;特别在检票环节&#xff0c;人工检票效率低、秩序混乱&#xff0c;导致常常出现检票口人山…

POL(Point-of-Load)负载点电源

负载点&#xff08;POL&#xff09;电源在靠近负载处单独放置电源调节器(线性稳压器或DC-DC)&#xff0c;解决了高性能半导体器件&#xff0c;例如&#xff1a;微控制器、ASIC等&#xff0c;所面临的高峰值电流、低噪声裕量等设计挑战。 一般我们会把负载点电源尽量靠近负载放…

乾元通多卡聚合技术在无人配送车应用领域通信保障方案

在无人驾驶公交车、安防车、售卖车、清扫车相继亮相后&#xff0c;无人配送车在全国各地也陆续“上岗”&#xff0c;为我们的城市带来了与众不同的“智慧体验”&#xff0c;让城市有了“科技温度”。 无人配送车在营业部装载好快递后&#xff0c;会按照提前规划好的路线出发&a…

sqli-labs靶场通关攻略 61-65

主页有sqli-labs靶场通关攻略 1-60 第六一关 less-61 步骤一&#xff1a;闭合方式&#xff1a;?id1)) -- 步骤二&#xff1a;查询数据库 ?id1)) and updatexml(1,concat(1,database()),1) -- 步骤三&#xff1a;查出网站的数据库表名 ?id1)) and updatexml(1,concat(0x7e…

15 用户管理

如果我们只能使用root用户&#xff0c;这样存在安全隐患。这时&#xff0c;就需要使用mysql的用户管理 张三只能操纵mytest这个库&#xff0c;李四只能操纵msg这个库&#xff0c;如果给他们root账户&#xff0c;就可以操纵所有库&#xff0c;风险太大 用户 用户信息 用户都存…

项目技巧三

目录 我们现在要实现一个接口功能 1.我们先书写sql语句 2.编写接口 3.书写业务逻辑 4.书写mapper 结果&#xff1a; 缺点&#xff1a;没有根据涨跌幅区间的大小来排序 1.yml文件 2.在value object包下映射这个yml文件 3.开启这个配置类进行映射&#xff0c;并把它交给s…

勒索攻击后快速恢复的八个关键步骤,如何避免被勒索攻击

勒索软件攻击依然是当今企业面临的最大安全威胁之一。根据德迅云安全收集的报告&#xff0c;59%的企业在2023年遭遇了勒索软件攻击&#xff0c;其中56%的受害者最终选择支付赎金以恢复数据。更为严重的是&#xff0c;63%的勒索金额达到或超过了100万美元&#xff0c;平均支付金…

RS232转RS485

1.232转485转换器 232转485转换器是RS-232与RS-485之间的双向接口的转换器&#xff0c;应用于主控机之间&#xff0c;主控机与单片机或外设之间构成点到点&#xff0c;点到多点远程多机通信网络&#xff0c;实现多机应答通信&#xff0c;广泛地应用于工业自动化控制系统&#x…

免费申请aws一年免费服务器使用教程

由于近期要测试一个公网项目&#xff0c;对比之下&#xff0c;选择了aws服务器&#xff0c;免费使用一年。 准备&#xff1a;一个visa信用卡即可&#xff0c;需要一个外网邮箱&#xff08;我这边使用的hotmail&#xff09; 注册的步骤不再赘述&#xff0c;切记几个点&#xff0…

windows 环境下安装OpenCV For Java

windows 环境下安装OpenCV For Java 进入官网下载对应安装包 opencv官网地址&#xff1a; https://opencv.org/releases/ source里面是官方的xml文件&#xff0c;包含人脸识别 windows下载下来是一个exe文件&#xff0c;里面包含项目需要的jar包 执行opencv-4.9.0-windows.ex…

被低估的SQL

SQL是现代数据库管理系统中不可或缺的一部分。尽管它的使用已十分普遍&#xff0c;但在数据处理领域&#xff0c;SQL的某些功能和潜力仍然被许多人低估。接下来&#xff0c;小编将与您一起&#xff0c;探讨SQL的一些被忽视的特性&#xff0c;揭示它在数据管理中的真正实力。 1.…

《深度学习》OpenCV轮廓检测 轮廓近似、模板匹配 解析及实现

一、轮廓近似 1、什么是轮廓近似 指对轮廓进行逼近或拟合&#xff0c;得到近似的轮廓。在图像处理中&#xff0c;轮廓表示了图像中物体的边界&#xff0c;因此轮廓近似可以用来描述和识别物体的形状。 2、参数解析 1&#xff09;用法 import cv2 approx cv2.approxPolyDP(cu…

Java SpringBoot构建助农平台,三步实现高效捐赠,2025届设计新思路!

✍✍计算机毕业编程指导师** ⭐⭐个人介绍&#xff1a;自己非常喜欢研究技术问题&#xff01;专业做Java、Python、微信小程序、安卓、大数据、爬虫、Golang、大屏等实战项目。 ⛽⛽实战项目&#xff1a;有源码或者技术上的问题欢迎在评论区一起讨论交流&#xff01; ⚡⚡ Java…

如何应对 Android 面试官 -> 内存如何进行优化?玩转 LeakCanary

前言 本章主要围绕内存相关的知识点讲解&#xff1b; 内存分配 在内存优化中&#xff0c;我们通常需要借助一些常用的 adb 命令&#xff0c;方便我们快速定位&#xff0c;下面是一些常用的 adb 命令总结 常用 adb 命令 adb shell getprop ro.product.model // 手机型号adb s…