當(dāng)前位置:首頁 > 公眾號(hào)精選 > Linux閱碼場(chǎng)
[導(dǎo)讀]本文介紹了動(dòng)態(tài)添加系統(tǒng)調(diào)用,即在不重新編譯內(nèi)核的前提下,添加系統(tǒng)調(diào)用。

添加新的系統(tǒng)調(diào)用 ,這是一個(gè)老掉牙的話題。前段時(shí)間折騰Rootkit的時(shí)候,我有意避開涉及HOOK劫持系統(tǒng)調(diào)用的話題,我主要是想來點(diǎn)新鮮的東西,畢竟關(guān)于劫持系統(tǒng)調(diào)用這種話題,網(wǎng)上的資料可謂汗牛充棟。

本文的主題依然不是劫持系統(tǒng)調(diào)用,而是添加系統(tǒng)調(diào)用,并且是動(dòng)態(tài)添加系統(tǒng)調(diào)用,即在不重新編譯內(nèi)核的前提下添加系統(tǒng)調(diào)用,畢竟如果可以重新編譯內(nèi)核的話,那實(shí)在是沒有意思。

但文中所述動(dòng)態(tài)新增系統(tǒng)調(diào)用的方式依然是老掉牙的方式,甚至和2011年的文章有所雷同,但是 這篇文章介紹的方式足夠清爽!

我們從一個(gè)問題開始。我的問題是:

  • Linux系統(tǒng)中如何獲取以及修改當(dāng)前進(jìn)程的名字??

你去搜一下這個(gè)topic,一堆冗余繁雜的方案,大多數(shù)都是借助procfs來完成這個(gè)需求,但沒有直接的讓人感到清爽的方法,比如調(diào)用一個(gè)getname接口即可獲取當(dāng)前進(jìn)程的名字,調(diào)用一個(gè)modname接口就能修改自己的名字,沒有這樣的方法。

所以,干嘛不增加兩個(gè)系統(tǒng)調(diào)用呢:

  • sys_getname: 獲取當(dāng)前進(jìn)程名。

  • sys_setname: 修改當(dāng)前進(jìn)程名。

總體上,這是一個(gè) 增加兩個(gè)系統(tǒng)調(diào)用的問題。

下面先演示動(dòng)態(tài)增加一個(gè)系統(tǒng)調(diào)用的原理。還是使用2011年的老例子,這次我簡(jiǎn)單點(diǎn),用systemtap腳本來實(shí)現(xiàn)。

千萬不要質(zhì)疑systemtap的威力,它的guru模式其實(shí)就是一個(gè)普通的內(nèi)核模塊,只是讓編程變得更簡(jiǎn)單,所以, 把systemtap當(dāng)一種方言來看待,而不僅僅作為調(diào)試探測(cè)工具。 甚至純guru模式的stap腳本根本沒有用到int 3斷點(diǎn),它簡(jiǎn)直可以用于線上生產(chǎn)環(huán)境!

演示增加系統(tǒng)調(diào)用的stap腳本如下:


  1. #!/usr/bin/stap -g
  2. // newsyscall.stap
  3. %{
  4. unsigned char *old_tbl;
  5. // 這里借用本module的地址,分配靜態(tài)數(shù)組new_tbl作為新的系統(tǒng)調(diào)用表。
  6. // 注意:不能調(diào)用kmalloc,vmalloc分配,因?yàn)樵趚86_64平臺(tái)它們的地址無法被內(nèi)核rel32跳轉(zhuǎn)過來!
  7. unsigned char new_tbl[8*500] = {0};
  8. unsigned long call_addr = 0;
  9. unsigned long nr_addr = 0;
  10. unsigned int off_old;
  11. unsigned short nr_old;
  12. // 使用內(nèi)核現(xiàn)成的poke text接口,而不是自己去修改頁表權(quán)限。
  13. // 當(dāng)然,也可以修改CR0,不過這顯然沒有直接用text_poke清爽。
  14. // 這是可行的,不然呢??jī)?nèi)核自己的ftrace或者live kpatch怎么辦?!
  15. void *(*_text_poke_smp)(void *addr, const void *opcode, size_t len);
  16. %}
  17. %{
  18. // 2011年文章里的例子,打印一句話而已,我修改了函數(shù)名字,稱作“皮鞋”
  19. asmlinkage long sys_skinshoe(int i)
  20. {
  21. printk("new call----:%d\n", i);
  22. return 0;
  23. }
  24. %}

  25. function syscall_table_poke()
  26. %{
  27. unsigned short nr_new = 0;
  28. unsigned int off_new = 0;
  29. unsigned char *syscall;
  30. unsigned long new_addr;
  31. int i;

  32. new_addr = (unsigned long)sys_skinshoe;
  33. syscall = (void *)kallsyms_lookup_name("system_call");
  34. old_tbl = (void *)kallsyms_lookup_name("sys_call_table");
  35. _text_poke_smp = (void *)kallsyms_lookup_name("text_poke_smp");

  36. // 拷貝原始的系統(tǒng)調(diào)用表,3200個(gè)字節(jié)有點(diǎn)多了,但絕對(duì)不會(huì)少。
  37. memcpy(&new_tbl[0], old_tbl, 3200);
  38. // 獲取新系統(tǒng)調(diào)用表的disp32偏移(x86_64帶符號(hào)擴(kuò)展)。
  39. off_new = (unsigned int)((unsigned long)&new_tbl[0]);

  40. // 在system_call函數(shù)的指令碼里進(jìn)行特征匹配,匹配cmp $0x143 %rax
  41. for (i = 0; i < 0xff; i++) {
  42. if (syscall[i] == 0x48 && syscall[i+1] == 0x3d) {
  43. nr_addr = (unsigned long)&syscall[i+2];
  44. break;
  45. }
  46. }
  47. // 在system_call函數(shù)的指令碼里進(jìn)行特征匹配,匹配callq *xxxxx(,%rax,8)
  48. for (i = 0; i < 0xff; i++) {
  49. if (syscall[i] == 0xff && syscall[i+1] == 0x14 && syscall[i+2] == 0xc5) {
  50. call_addr = (unsigned long)&syscall[i+3];
  51. break;
  52. }
  53. }
  54. // 1. 增加一個(gè)系統(tǒng)調(diào)用數(shù)量
  55. // 2. 使能新的系統(tǒng)調(diào)用表
  56. off_old = *(unsigned int *)call_addr;
  57. nr_old = *(unsigned short *)nr_addr;
  58. // 設(shè)置新的系統(tǒng)調(diào)用入口函數(shù)
  59. *(unsigned long *)&new_tbl[nr_old*8 + 8] = new_addr;
  60. nr_new = nr_old + 1;
  61. memcpy(&new_tbl[nr_new*8 + 8], &old_tbl[nr_old*8 + 8], 16);
  62. // poke 代碼
  63. _text_poke_smp((void *)nr_addr, &nr_new, 2);
  64. _text_poke_smp((void *)call_addr, &off_new, 4);
  65. %}

  66. function syscall_table_clean()
  67. %{
  68. _text_poke_smp((void *)nr_addr, &nr_old, 2);
  69. _text_poke_smp((void *)call_addr, &off_old, 4);
  70. %}

  71. probe begin
  72. {
  73. syscall_table_poke();
  74. }
  75. probe end
  76. {
  77. syscall_table_clean();
  78. }

唯一需要解釋的就是兩處poke:

  1. 修改系統(tǒng)調(diào)用數(shù)量的限制。

  2. 修改系統(tǒng)調(diào)用表的位置。

我們從system_call指令碼中一看便知:


  1. crash> dis system_call
  2. 0xffffffff81645110 <system_call>: swapgs
  3. ...
  4. # 0x143需要修改為0x144
  5. 0xffffffff81645173 <system_call_fastpath>: cmp $0x143,%rax
  6. 0xffffffff81645179 <system_call_fastpath+6>: ja 0xffffffff81645241 <badsys>
  7. 0xffffffff8164517f <system_call_fastpath+12>: mov %r10,%rcx
  8. # -0x7e9b2c40需要被修正為新系統(tǒng)調(diào)用表的disp32偏移
  9. 0xffffffff81645182 <system_call_fastpath+15>: callq *-0x7e9b2c40(,%rax,8)
  10. 0xffffffff81645189 <system_call_fastpath+22>: mov %rax,0x20(%rsp)

如果代碼正常,那么直接執(zhí)行上面的stap腳本的話,新的系統(tǒng)調(diào)用應(yīng)該已經(jīng)生成,它的系統(tǒng)調(diào)用號(hào)為324,也就是0x143+1。至于說為什么系統(tǒng)調(diào)用號(hào)必須是逐漸遞增的,請(qǐng)看:

  1. callq *-0x7e9b2c40(,%rax,8)

上述代碼的含義是:


  1. call index * 8 + disp32_offset

這意味著內(nèi)核是按照數(shù)組下標(biāo)的方式索引系統(tǒng)調(diào)用的,這要求它們必須連續(xù)存放。

好了,回到現(xiàn)實(shí),我們上面的行動(dòng)是否成功了呢?事情到底是不是我們想象的那樣的呢?我們寫個(gè)測(cè)試case驗(yàn)證一下:


  1. // newcall.c
  2. int main(int argc, char *argv[])
  3. {
  4. syscall(324, 1234);
  5. perror("new system call");
  6. }

執(zhí)行之,看結(jié)果:


  1. [root@localhost test]# gcc newcall.c
  2. [root@localhost test]# ./a.out
  3. new system call: Success
  4. [root@localhost test]# dmesg
  5. [ 1547.387847] stap_6874ae02ddb22b6650aee5cd2e080b49_2209: systemtap: 3.3/0.176, base: ffffffffa03b6000, memory: 106data/24text/0ctx/2063net/9alloc kb, probes: 2
  6. [ 1549.119316] new call----:1234

OK,成功!此時(shí)我們Ctrl-C掉我們的stap腳本,再次執(zhí)行a.out:


  1. [root@localhost test]# ./a.out
  2. new system call: Function not implemented

完全符合預(yù)期。


OK,那么現(xiàn)在開始正事,即新增兩個(gè)系統(tǒng)調(diào)用,sysgetname和syssetname,分別為獲取和設(shè)置當(dāng)前進(jìn)程的名字。

來吧,讓我們開始。

其實(shí) newsyscall.stap 已經(jīng)足夠了,稍微改一下即可,但是這里的 稍微改 體現(xiàn)了品質(zhì)和優(yōu)雅:

  • 改為oneshot模式,畢竟我不希望有個(gè)模塊在系統(tǒng)里。

oneshot模式需要?jiǎng)討B(tài)分配內(nèi)存,保證在stap模塊退出后這塊內(nèi)存不會(huì)隨著模塊的卸載而自動(dòng)釋放。而這個(gè),我已經(jīng)玩膩了。

直接上代碼:


  1. #!/usr/bin/stap -g
  2. // poke.stp
  3. %{
  4. // 為了rel32偏移的可達(dá)性,借用模塊映射空間的范圍來分配內(nèi)存。
  5. #define START _AC(0xffffffffa0000000, UL)
  6. #define END _AC(0xffffffffff000000, UL)

  7. // 保存原始的系統(tǒng)調(diào)用表。
  8. unsigned char *old_tbl;
  9. // 保存新的系統(tǒng)調(diào)用表。
  10. unsigned char *new_tbl;
  11. // call系統(tǒng)調(diào)用表的位置。
  12. unsigned long call_addr = 0;
  13. // 系統(tǒng)調(diào)用數(shù)量限制檢查的位置。
  14. unsigned long nr_addr = 0;
  15. // 原始的系統(tǒng)調(diào)用表disp32偏移。
  16. unsigned int off_old;
  17. // 原始的系統(tǒng)調(diào)用數(shù)量。
  18. unsigned short nr_old;
  19. void * *(*___vmalloc_node_range)(unsigned long, unsigned long,
  20. unsigned long, unsigned long, gfp_t,
  21. pgprot_t, int, const void *);
  22. void *(*_text_poke_smp)(void *addr, const void *opcode, size_t len);
  23. %}
  24. %{

  25. // 新系統(tǒng)調(diào)用的text被copy到了新的頁面,因此最好不要調(diào)用內(nèi)核函數(shù)。

  26. // 這是因?yàn)閮?nèi)核函數(shù)之間的互調(diào)使用的是rel32調(diào)用,這就需要校準(zhǔn)偏移,太麻煩。
  27. // 記?。鹤鳛槔?,不調(diào)用printk,也不調(diào)用memcpy/memset...如果想秀花活兒,自己去校準(zhǔn)吧。
  28. // 詳細(xì)的秀法,參見我前面關(guān)于rootkit的文章。
  29. long sys_setskinshoe(char *newname, unsigned int len)
  30. {
  31. int i;
  32. if (len > 16 - 1)
  33. return -1;

  34. for (i = 0; i < len; i++) {
  35. current->comm[i] = newname[i];
  36. }
  37. current->comm[i] = 0;
  38. return 0;
  39. }

  40. long sys_getskinshoe(char *name, unsigned int len)
  41. {
  42. int i;

  43. if (len > 16 - 1)
  44. return -1;

  45. for (i = 0; i < len; i++) {
  46. name[i] = current->comm[i];
  47. }
  48. return 0;
  49. }
  50. unsigned char *stub_sys_skinshoe;
  51. %}

  52. function syscall_table_poke()
  53. %{
  54. unsigned short nr_new = 0;
  55. unsigned int off_new = 0;
  56. unsigned char *syscall;
  57. unsigned long new_addr;
  58. int i;

  59. syscall = (void *)kallsyms_lookup_name("system_call");
  60. old_tbl = (void *)kallsyms_lookup_name("sys_call_table");
  61. ___vmalloc_node_range = (void *)kallsyms_lookup_name("__vmalloc_node_range");
  62. _text_poke_smp = (void *)kallsyms_lookup_name("text_poke_smp");

  63. new_tbl = (void *)___vmalloc_node_range(8*500, 1, START, END,
  64. GFP_KERNEL | __GFP_HIGHMEM, PAGE_KERNEL_EXEC,
  65. -1, NULL/*__builtin_return_address(0)*/);
  66. stub_sys_skinshoe = (void *)___vmalloc_node_range(0xff, 1, START, END,
  67. GFP_KERNEL | __GFP_HIGHMEM, PAGE_KERNEL_EXEC,
  68. -1, NULL);
  69. // 拷貝代碼指令
  70. memcpy(&stub_sys_skinshoe[0], sys_setskinshoe, 90);
  71. memcpy(&stub_sys_skinshoe[96], sys_getskinshoe, 64);
  72. // 拷貝系統(tǒng)調(diào)用表
  73. memcpy(&new_tbl[0], old_tbl, 3200);
  74. new_addr = (unsigned long)&stub_sys_skinshoe[0];
  75. off_new = (unsigned int)((unsigned long)&new_tbl[0]);
  76. // cmp指令匹配
  77. for (i = 0; i < 0xff; i++) {
  78. if (syscall[i] == 0x48 && syscall[i+1] == 0x3d) {
  79. nr_addr = (unsigned long)&syscall[i+2];
  80. break;
  81. }
  82. }
  83. // call指令匹配
  84. for (i = 0; i < 0xff; i++) {
  85. if (syscall[i] == 0xff && syscall[i+1] == 0x14 && syscall[i+2] == 0xc5) {
  86. call_addr = (unsigned long)&syscall[i+3];
  87. break;
  88. }
  89. }

  90. off_old = *(unsigned int *)call_addr;
  91. nr_old = *(unsigned short *)nr_addr;
  92. // 設(shè)置setskinshoe
  93. *(unsigned long *)&new_tbl[nr_old*8 + 8] = new_addr;
  94. new_addr = (unsigned long)&stub_sys_skinshoe[96];
  95. // 設(shè)置getskinshoe
  96. *(unsigned long *)&new_tbl[nr_old*8 + 8 + 8] = new_addr;
  97. // 系統(tǒng)調(diào)用數(shù)量增加2個(gè)
  98. nr_new = nr_old + 2;
  99. // 后移tail stub
  100. memcpy(&new_tbl[nr_new*8 + 8], &old_tbl[nr_old*8 + 8], 16);
  101. _text_poke_smp((void *)nr_addr, &nr_new, 2);
  102. _text_poke_smp((void *)call_addr, &off_new, 4);
  103. // 至此,新的系統(tǒng)調(diào)用表已經(jīng)生效,盡情修改吧!
  104. %}
  105. probe begin
  106. {
  107. syscall_table_poke();
  108. exit();
  109. }

順便,我把恢復(fù)原始系統(tǒng)調(diào)用表的操作腳本也附帶上:


  1. #!/usr/bin/stap -g
  2. // revert.stp
  3. %{
  4. void *(*_text_poke_smp)(void *addr, const void *opcode, size_t len);
  5. %}
  6. function syscall_table_revert()
  7. %{
  8. unsigned int off_new, off_old;
  9. unsigned char *syscall;
  10. unsigned long nr_addr = 0, call_addr = 0, orig_addr, *new_tbl;
  11. // 0x143這個(gè)還是記在腦子里吧.
  12. unsigned short nr_calls = 0x0143, curr_calls;
  13. int i;

  14. syscall = (void *)kallsyms_lookup_name("system_call");
  15. orig_addr = (unsigned long)kallsyms_lookup_name("sys_call_table");
  16. _text_poke_smp = (void *)kallsyms_lookup_name("text_poke_smp");

  17. for (i = 0; i < 0xff; i++) {
  18. if (syscall[i] == 0x48 && syscall[i+1] == 0x3d) {
  19. nr_addr = (unsigned long)&syscall[i+2];
  20. break;
  21. }
  22. }
  23. for (i = 0; i < 0xff; i++) {
  24. if (syscall[i] == 0xff && syscall[i+1] == 0x14 && syscall[i+2] == 0xc5) {
  25. call_addr = (unsigned long)&syscall[i+3];
  26. break;
  27. }
  28. }
  29. curr_calls = *(unsigned short *)nr_addr;
  30. off_new = *(unsigned int *)call_addr;
  31. off_old = (unsigned int)orig_addr;
  32. // decode出自己的系統(tǒng)調(diào)用表的地址。
  33. new_tbl = (unsigned long *)(0xffffffff00000000 | off_new);
  34. _text_poke_smp((void *)nr_addr, &nr_calls, 2);
  35. _text_poke_smp((void *)call_addr, &off_old, 4);

  36. vfree((void *)new_tbl[nr_calls + 1]);
  37. /*
  38. // loop free
  39. // 如果你增加的系統(tǒng)調(diào)用比較多,且分布在不同的malloc頁面,那么就需要循環(huán)free
  40. for (i = 0; i < curr_calls - nr_calls; i ++) {
  41. vfree((void *)new_tbl[nr_calls + 1 + i]);
  42. }
  43. */
  44. // 釋放自己的系統(tǒng)調(diào)用表
  45. vfree((void *)new_tbl);
  46. %}

  47. probe begin
  48. {
  49. syscall_table_revert();
  50. exit();
  51. }

來吧,開始我們的實(shí)驗(yàn)!

我不懂編程,所以我只能寫最簡(jiǎn)單的代碼展示效果,下面的C代碼直接調(diào)用新增的兩個(gè)系統(tǒng)調(diào)用,首先它獲得并打印自己的名字,然后把名字改掉,最后再次獲取并打印自己的名字:


  1. #include
  2. #include
  3. #include

  4. int main(int argc, char *argv[])
  5. {
  6. char name[16] = {0};
  7. syscall(325, name, 12);
  8. perror("-- get name before");
  9. printf("my name is %s\n", name);
  10. syscall(324, argv[1], strlen(argv[1]));
  11. perror("-- Modify name");
  12. syscall(325, name, 12);
  13. perror("-- get name after");
  14. printf("my name is %s\n", name);
  15. return 0;
  16. }

下面是實(shí)驗(yàn)結(jié)果:


  1. # 未poke時(shí)的結(jié)果
  2. [root@localhost test]# ./test_newcall skinshoe
  3. -- get name before: Function not implemented
  4. my name is
  5. -- Modify name: Function not implemented
  6. -- get name after: Function not implemented
  7. my name is
  8. [root@localhost test]#
  9. [root@localhost test]# ./poke.stp
  10. [root@localhost test]#
  11. # poke之后的結(jié)果,此時(shí)lsmod,你將看不到任何和這個(gè)poke相關(guān)的內(nèi)核模塊,這就是oneshot的效果。
  12. [root@localhost test]# ./test_newcall skinshoe
  13. -- get name before: Success
  14. my name is test_newcall
  15. -- Modify name: Success
  16. -- get name after: Success
  17. my name is skinshoe
  18. [root@localhost test]#
  19. [root@localhost test]# ./revert.stp
  20. [root@localhost test]#
  21. # revert之后的結(jié)果
  22. [root@localhost test]# ./test_newcall skinshoe
  23. -- get name before: Function not implemented
  24. my name is
  25. -- Modify name: Function not implemented
  26. -- get name after: Function not implemented
  27. my name is
  28. [root@localhost test]#

足夠簡(jiǎn)單,足夠直接,工人們和經(jīng)理都可以上手一試。

我們?nèi)绻屝略龅南到y(tǒng)調(diào)用干點(diǎn)壞事,那再簡(jiǎn)單不過了,得手之后呢?如何防止被經(jīng)理抓到呢?封堵模塊加載的接口即可咯,反正不加載內(nèi)核模塊,誰也別想看到當(dāng)前系統(tǒng)的內(nèi)核被hack成了什么樣子,哦,對(duì)了,把/dev/mem的mmap也堵死哦...

....不過這是下面文章的主題了。

好了,今天就先寫到這兒吧。


免責(zé)聲明:本文內(nèi)容由21ic獲得授權(quán)后發(fā)布,版權(quán)歸原作者所有,本平臺(tái)僅提供信息存儲(chǔ)服務(wù)。文章僅代表作者個(gè)人觀點(diǎn),不代表本平臺(tái)立場(chǎng),如有問題,請(qǐng)聯(lián)系我們,謝謝!

本站聲明: 本文章由作者或相關(guān)機(jī)構(gòu)授權(quán)發(fā)布,目的在于傳遞更多信息,并不代表本站贊同其觀點(diǎn),本站亦不保證或承諾內(nèi)容真實(shí)性等。需要轉(zhuǎn)載請(qǐng)聯(lián)系該專欄作者,如若文章內(nèi)容侵犯您的權(quán)益,請(qǐng)及時(shí)聯(lián)系本站刪除。
換一批
延伸閱讀

9月2日消息,不造車的華為或?qū)⒋呱龈蟮莫?dú)角獸公司,隨著阿維塔和賽力斯的入局,華為引望愈發(fā)顯得引人矚目。

關(guān)鍵字: 阿維塔 塞力斯 華為

倫敦2024年8月29日 /美通社/ -- 英國汽車技術(shù)公司SODA.Auto推出其旗艦產(chǎn)品SODA V,這是全球首款涵蓋汽車工程師從創(chuàng)意到認(rèn)證的所有需求的工具,可用于創(chuàng)建軟件定義汽車。 SODA V工具的開發(fā)耗時(shí)1.5...

關(guān)鍵字: 汽車 人工智能 智能驅(qū)動(dòng) BSP

北京2024年8月28日 /美通社/ -- 越來越多用戶希望企業(yè)業(yè)務(wù)能7×24不間斷運(yùn)行,同時(shí)企業(yè)卻面臨越來越多業(yè)務(wù)中斷的風(fēng)險(xiǎn),如企業(yè)系統(tǒng)復(fù)雜性的增加,頻繁的功能更新和發(fā)布等。如何確保業(yè)務(wù)連續(xù)性,提升韌性,成...

關(guān)鍵字: 亞馬遜 解密 控制平面 BSP

8月30日消息,據(jù)媒體報(bào)道,騰訊和網(wǎng)易近期正在縮減他們對(duì)日本游戲市場(chǎng)的投資。

關(guān)鍵字: 騰訊 編碼器 CPU

8月28日消息,今天上午,2024中國國際大數(shù)據(jù)產(chǎn)業(yè)博覽會(huì)開幕式在貴陽舉行,華為董事、質(zhì)量流程IT總裁陶景文發(fā)表了演講。

關(guān)鍵字: 華為 12nm EDA 半導(dǎo)體

8月28日消息,在2024中國國際大數(shù)據(jù)產(chǎn)業(yè)博覽會(huì)上,華為常務(wù)董事、華為云CEO張平安發(fā)表演講稱,數(shù)字世界的話語權(quán)最終是由生態(tài)的繁榮決定的。

關(guān)鍵字: 華為 12nm 手機(jī) 衛(wèi)星通信

要點(diǎn): 有效應(yīng)對(duì)環(huán)境變化,經(jīng)營業(yè)績(jī)穩(wěn)中有升 落實(shí)提質(zhì)增效舉措,毛利潤(rùn)率延續(xù)升勢(shì) 戰(zhàn)略布局成效顯著,戰(zhàn)新業(yè)務(wù)引領(lǐng)增長(zhǎng) 以科技創(chuàng)新為引領(lǐng),提升企業(yè)核心競(jìng)爭(zhēng)力 堅(jiān)持高質(zhì)量發(fā)展策略,塑強(qiáng)核心競(jìng)爭(zhēng)優(yōu)勢(shì)...

關(guān)鍵字: 通信 BSP 電信運(yùn)營商 數(shù)字經(jīng)濟(jì)

北京2024年8月27日 /美通社/ -- 8月21日,由中央廣播電視總臺(tái)與中國電影電視技術(shù)學(xué)會(huì)聯(lián)合牽頭組建的NVI技術(shù)創(chuàng)新聯(lián)盟在BIRTV2024超高清全產(chǎn)業(yè)鏈發(fā)展研討會(huì)上宣布正式成立。 活動(dòng)現(xiàn)場(chǎng) NVI技術(shù)創(chuàng)新聯(lián)...

關(guān)鍵字: VI 傳輸協(xié)議 音頻 BSP

北京2024年8月27日 /美通社/ -- 在8月23日舉辦的2024年長(zhǎng)三角生態(tài)綠色一體化發(fā)展示范區(qū)聯(lián)合招商會(huì)上,軟通動(dòng)力信息技術(shù)(集團(tuán))股份有限公司(以下簡(jiǎn)稱"軟通動(dòng)力")與長(zhǎng)三角投資(上海)有限...

關(guān)鍵字: BSP 信息技術(shù)
關(guān)閉
關(guān)閉