Ibrahim Said Ahmad

58 papers A* 2B 3Journal 40Unranked 13
YearRankTypeTitle / Venue / Authors
2025 conf
NAACL (Long Papers)
Shamsuddeen Hassan Muhammad, Idris Abdulmumin, Abinew Ali Ayele, David Ifeoluwa Adelani, Ibrahim Said Ahmad, Saminu Mohammad Aliyu, Paul Röttger, Abigail Oppong, Andiswa Bukula, Chiamaka Ijeoma Chukwuneke, Ebrahim Chekol Jibril, Elyas Abdi Ismail, Esubalew Alemneh, Hagos Tesfahun Gebremichael, Lukman Jibril Aliyu, Meriem Beloucif, Oumaima Hourrane, Rooweither Mabuya, Salomey Osei, Samuel Rutunda, Tadesse Destaw Belay, Tadesse Kebede Guge, Tesfa Tegegne Asfaw, Lilian Diana Awuor Wanzare, Nelson Odhiambo Onyango, Seid Muhie Yimam, Nedjma Ousidhoum
2025 J jnl
CoRR
Shamsuddeen Hassan Muhammad, Idris Abdulmumin, Abinew Ali Ayele, David Ifeoluwa Adelani, Ibrahim Said Ahmad, Saminu Mohammad Aliyu, Nelson Odhiambo Onyango, Lilian Diana Awuor Wanzare, Samuel Rutunda, Lukman Jibril Aliyu, Esubalew Alemneh, Oumaima Hourrane, Hagos Tesfahun Gebremichael, Elyas Abdi Ismail, Meriem Beloucif, Ebrahim Chekol Jibril, Andiswa Bukula, Rooweither Mabuya, Salomey Osei, Abigail Oppong, Tadesse Destaw Belay, Tadesse Kebede Guge, Tesfa Tegegne Asfaw, Chiamaka Ijeoma Chukwuneke, Paul Röttger, Seid Muhie Yimam, Nedjma Ousidhoum
2025 conf
EMNLP (Findings)
Tadesse Destaw Belay, Israel Abebe Azime, Ibrahim Said Ahmad, David Ifeoluwa Adelani, Idris Abdulmumin, Abinew Ali Ayele, Shamsuddeen Hassan Muhammad, Seid Muhie Yimam
2025 J jnl
CoRR
Tadesse Destaw Belay, Israel Abebe Azime, Ibrahim Said Ahmad, Idris Abdulmumin, Abinew Ali Ayele, Shamsuddeen Hassan Muhammad, Seid Muhie Yimam
2025 J jnl
CoRR
Sukairaj Hafiz Imam, Tadesse Destaw Belay, Kedir Yassin Hussen, Ibrahim Said Ahmad, Idris Abdulmumin, Hadiza Ali Umar, Muhammad Yahuza Bello, Joyce Nakatumba-Nabende, Seid Muhie Yimam, Shamsuddeen Hassan Muhammad
2025 J jnl
CoRR
Sukairaj Hafiz Imam, Babangida Sani, Dawit Ketema Gete, Bedru Yimam Ahamed, Ibrahim Said Ahmad, Idris Abdulmumin, Seid Muhie Yimam, Muhammad Yahuza Bello, Shamsuddeen Hassan Muhammad
2025 conf
ACL (1)
Shamsuddeen Hassan Muhammad, Nedjma Ousidhoum, Idris Abdulmumin, Jan Philip Wahle, Terry Ruas, Meriem Beloucif, Christine de Kock, Nirmal Surange, Daniela Teodorescu, Ibrahim Said Ahmad, David Ifeoluwa Adelani, Alham Fikri Aji, Felermino D. M. A. Ali, Ilseyar Alimova, Vladimir Araujo, Nikolay Babakov, Naomi Baes, Ana-Maria Bucur, Andiswa Bukula, Guanqun Cao, Rodrigo Tufiño Cardenas, Rendi Chevi, Chiamaka Ijeoma Chukwuneke, Alexandra Ciobotaru, Daryna Dementieva, Murja Sani Gadanya, Robert Geislinger, Bela Gipp, Oumaima Hourrane, Oana Ignat, Falalu Ibrahim Lawan, Rooweither Mabuya, Rahmad Mahendra, Vukosi Marivate, Alexander Panchenko, Andrew Piper, Charles Henrique Porto Ferreira, Vitaly Protasov, Samuel Rutunda, Manish Shrivastava, Aura Cristina Udrea, Lilian Diana Awuor Wanzare, Sophie Wu, Florian Valentin Wunderlich, Hanif Muhammad Zhafran, Tianhui Zhang, Yi Zhou, Saif M. Mohammad
2025 J jnl
CoRR
Shamsuddeen Hassan Muhammad, Nedjma Ousidhoum, Idris Abdulmumin, Jan Philip Wahle, Terry Ruas, Meriem Beloucif, Christine de Kock, Nirmal Surange, Daniela Teodorescu, Ibrahim Said Ahmad, David Ifeoluwa Adelani, Alham Fikri Aji, Felermino Dário Mário António Ali, Ilseyar Alimova, Vladimir Araujo, Nikolay Babakov, Naomi Baes, Ana-Maria Bucur, Andiswa Bukula, Guanqun Cao, Rodrigo Tufiño Cardenas, Rendi Chevi, Chiamaka Ijeoma Chukwuneke, Alexandra Ciobotaru, Daryna Dementieva, Murja Sani Gadanya, Robert Geislinger, Bela Gipp, Oumaima Hourrane, Oana Ignat, Falalu Ibrahim Lawan, Rooweither Mabuya, Rahmad Mahendra, Vukosi Marivate, Andrew Piper, Alexander Panchenko, Charles Henrique Porto Ferreira, Vitaly Protasov, Samuel Rutunda, Manish Shrivastava, Aura Cristina Udrea, Lilian Diana Awuor Wanzare, Sophie Wu, Florian Valentin Wunderlich, Hanif Muhammad Zhafran, Tianhui Zhang, Yi Zhou, Saif M. Mohammad
2025 J jnl
CoRR
Ibrahim Said Ahmad, Shiran Dudy, Tadesse Destaw Belay, Idris Abdulmumin, Seid Muhie Yimam, Shamsuddeen Hassan Muhammad, Kenneth Church
2025 J jnl
CoRR
Abdulhamid Abubakar, Hamidatu Abdulkadir, Rabiu Ibrahim Abdullahi, Auwal Abubakar Khalid, Ahmad Mustapha Wali, Amina Aminu Umar, Maryam Bala, Sani Abdullahi Sani, Ibrahim Said Ahmad, Shamsuddeen Hassan Muhammad, Idris Abdulmumin, Vukosi Marivate
2025 J jnl
CoRR
Maryam Bala, Amina Imam Abubakar, Abdulhamid Abubakar, Abdulkadir Shehu Bichi, Hafsa Kabir Ahmad, Sani Abdullahi Sani, Idris Abdulmumin, Shamsuddeen Hassan Muhammad, Ibrahim Said Ahmad
2025 J jnl
CoRR
Shamsuddeen Hassan Muhammad, Ibrahim Said Ahmad, Idris Abdulmumin, Falalu Ibrahim Lawan, Babangida Sani, Sukairaj Hafiz Imam, Aliyu Yusuf, Sani Abdullahi Sani, Ali Usman Umar, Tajuddeen Gwadabe, Kenneth Church, Vukosi Marivate
2025 J jnl
Electron. Commer. Res.
Ismail Abdulrashid, Ibrahim Said Ahmad, Aminu Musa, Mohammed Khalafalla
2025 B conf
COLING
Kenneth Ward Church, Raman Chandrasekar, John E. Ortega, Ibrahim Said Ahmad
2025 J jnl
CoRR
Usman Naseem, Juan Ren, Saba Anwar, Sarah Kohail, Rudy Alexandro Garrido Veliz, Robert Geislinger, Aisha Jabr, Idris Abdulmumin, Laiba Qureshi, Aarushi Ajay Borkar, Maryam Ibrahim Mukhtar, Abinew Ali Ayele, Ibrahim Said Ahmad, Adem Chanie Ali, Martin Semmann, Shamsuddeen Hassan Muhammad, Seid Muhie Yimam
2025 J jnl
CoRR
Shamsuddeen Hassan Muhammad, Nedjma Ousidhoum, Idris Abdulmumin, Seid Muhie Yimam, Jan Philip Wahle, Terry Ruas, Meriem Beloucif, Christine de Kock, Tadesse Destaw Belay, Ibrahim Said Ahmad, Nirmal Surange, Daniela Teodorescu, David Ifeoluwa Adelani, Alham Fikri Aji, Felermino Ali, Vladimir Araujo, Abinew Ali Ayele, Oana Ignat, Alexander Panchenko, Yi Zhou, Saif M. Mohammad
2025 J jnl
CoRR
Tadesse Destaw Belay, Kedir Yassin Hussen, Sukairaj Hafiz Imam, Ibrahim Said Ahmad, Isa Inuwa-Dutse, Abrham Belete Haile, Grigori Sidorov, Iqra Ameer, Idris Abdulmumin, Tajuddeen Gwadabe, Vukosi Marivate, Seid Muhie Yimam, Shamsuddeen Hassan Muhammad
2025 J jnl
CoRR
Babangida Sani, Aakansha Soy, Sukairaj Hafiz Imam, Ahmad Mustapha, Lukman Jibril Aliyu, Idris Abdulmumin, Ibrahim Said Ahmad, Shamsuddeen Hassan Muhammad
2024 J jnl
CoRR
Ibrahim Said Ahmad, Lukman Jibril Aliyu, Auwal Abubakar Khalid, Saminu Mohammad Aliyu, Shamsuddeen Hassan Muhammad, Idris Abdulmumin, Bala Mairiga Abduljalil, Bello Shehu Bello, Amina Abubakar Imam
2024 B conf
ACII
Shiran Dudy, Ibrahim Said Ahmad, Ryoko Kitajima, Àgata Lapedriza
2024 J jnl
CoRR
Shiran Dudy, Ibrahim Said Ahmad, Ryoko Kitajima, Àgata Lapedriza
2024 J jnl
CoRR
Ibrahim Said Ahmad, Shiran Dudy, Resmi Ramachandranpillai, Kenneth Church
2024 conf
WMT
Idris Abdulmumin, Sthembiso Mkhwanazi, Mahlatse S. Mbooi, Shamsuddeen Hassan Muhammad, Ibrahim Said Ahmad, Neo Putini, Miehleketo Mathebula, Matimba Shingange, Tajuddeen Gwadabe, Vukosi Marivate
2024 J jnl
CoRR
Idris Abdulmumin, Sthembiso Mkhwanazi, Mahlatse S. Mbooi, Shamsuddeen Hassan Muhammad, Ibrahim Said Ahmad, Neo Putini, Miehleketo Mathebula, Matimba Shingange, Tajuddeen Gwadabe, Vukosi Marivate
2024 conf
WMT
Shantipriya Parida, Ondrej Bojar, Idris Abdulmumin, Shamsuddeen Hassan Muhammad, Ibrahim Said Ahmad
2024 J jnl
CoRR
Ibrahim Said Ahmad, Antonios Anastasopoulos, Ondrej Bojar, Claudia Borg, Marine Carpuat, Roldano Cattoni, Mauro Cettolo, William Chen, Qianqian Dong, Marcello Federico, Barry Haddow, Dávid Javorský, Mateusz Krubinski, Tsz Kin Lam, Xutai Ma, Prashant Mathur, Evgeny Matusov, Chandresh Maurya, John P. McCrae, Kenton Murray, Satoshi Nakamura, Matteo Negri, Jan Niehues, Xing Niu, Atul Kr. Ojha, John E. Ortega, Sara Papi, Peter Polák, Adam Pospísil, Pavel Pecina, Elizabeth Salesky, Nivedita Sethiya, Balaram Sarkar, Jiatong Shi, Claytone Sikasote, Matthias Sperber, Sebastian Stüker, Katsuhito Sudoh, Brian Thompson, Marco Turchi, Alex Waibel, Shinji Watanabe, Patrick Wilken, Petr Zemánek, Rodolfo Zevallos
2024 J jnl
CoRR
Kenneth Church, Raman Chandrasekar, John E. Ortega, Ibrahim Said Ahmad
2024 conf
LREC/COLING
Garry Kuwanto, Eno-Abasi Urua, Priscilla Amondi Amuok, Shamsuddeen Hassan Muhammad, Aremu Anuoluwapo, Verrah Otiende, Loice Emma Nanyanga, Teresiah W. Nyoike, Aniefon Daniel Akpan, Nsima Ab Udouboh, Idongesit Udeme Archibong, Idara Effiong Moses, Ifeoluwatayo A. Ige, Benjamin Ajibade, Olumide Benjamin Awokoya, Idris Abdulmumin, Saminu Mohammad Aliyu, Ruqayya Nasir Iro, Ibrahim Said Ahmad, Deontae Smith, Praise-EL Michaels, David Ifeoluwa Adelani, Derry Tanti Wijaya, Anietie Andy
2024 J jnl
CoRR
Garry Kuwanto, Eno-Abasi Urua, Priscilla Amondi Amuok, Shamsuddeen Hassan Muhammad, Anuoluwapo Aremu, Verrah Otiende, Loice Emma Nanyanga, Teresiah W. Nyoike, Aniefon Daniel Akpan, Nsima Ab Udouboh, Idongesit Udeme Archibong, Idara Effiong Moses, Ifeoluwatayo A. Ige, Benjamin Ajibade, Olumide Benjamin Awokoya, Idris Abdulmumin, Saminu Mohammad Aliyu, Ruqayya Nasir Iro, Ibrahim Said Ahmad, Deontae Smith, Praise-EL Michaels, David Ifeoluwa Adelani, Derry Tanti Wijaya, Anietie Andy
2024 A* conf
EMNLP
Youssef Mohamed, Runjia Li, Ibrahim Said Ahmad, Kilichbek Haydarov, Philip Torr, Kenneth Church, Mohamed Elhoseiny
2024 J jnl
CoRR
Youssef Mohamed, Runjia Li, Ibrahim Said Ahmad, Kilichbek Haydarov, Philip Torr, Kenneth Ward Church, Mohamed Elhoseiny
2024 J jnl
CoRR
John E. Ortega, Ibrahim Said Ahmad, William Chen
2024 conf
SemEval@NAACL
Nedjma Ousidhoum, Shamsuddeen Hassan Muhammad, Mohamed Abdalla, Idris Abdulmumin, Ibrahim Said Ahmad, Sanchit Ahuja, Alham Fikri Aji, Vladimir Araujo, Meriem Beloucif, Christine de Kock, Oumaima Hourrane, Manish Shrivastava, Thamar Solorio, Nirmal Surange, Krishnapriya Vishnubhotla, Seid Muhie Yimam, Saif M. Mohammad
2024 J jnl
CoRR
Nedjma Ousidhoum, Shamsuddeen Hassan Muhammad, Mohamed Abdalla, Idris Abdulmumin, Ibrahim Said Ahmad, Sanchit Ahuja, Alham Fikri Aji, Vladimir Araujo, Meriem Beloucif, Christine de Kock, Oumaima Hourrane, Manish Shrivastava, Thamar Solorio, Nirmal Surange, Krishnapriya Vishnubhotla, Seid Muhie Yimam, Saif M. Mohammad
2024 conf
ACL (Findings)
Nedjma Ousidhoum, Shamsuddeen Hassan Muhammad, Mohamed Abdalla, Idris Abdulmumin, Ibrahim Said Ahmad, Sanchit Ahuja, Alham Fikri Aji, Vladimir Araujo, Abinew Ali Ayele, Pavan Baswani, Meriem Beloucif, Chris Biemann, Sofia Bourhim, Christine de Kock, Genet Shanko Dekebo, Oumaima Hourrane, Gopichand Kanumolu, Lokesh Madasu, Samuel Rutunda, Manish Shrivastava, Thamar Solorio, Nirmal Surange, Hailegnaw Getaneh Tilaye, Krishnapriya Vishnubhotla, Genta Indra Winata, Seid Muhie Yimam, Saif M. Mohammad
2024 J jnl
CoRR
Nedjma Ousidhoum, Shamsuddeen Hassan Muhammad, Mohamed Abdalla, Idris Abdulmumin, Ibrahim Said Ahmad, Sanchit Ahuja, Alham Fikri Aji, Vladimir Araujo, Abinew Ali Ayele, Pavan Baswani, Meriem Beloucif, Chris Biemann, Sofia Bourhim, Christine de Kock, Genet Shanko Dekebo, Oumaima Hourrane, Gopichand Kanumolu, Lokesh Madasu, Samuel Rutunda, Manish Shrivastava, Thamar Solorio, Nirmal Surange, Hailegnaw Getaneh Tilaye, Krishnapriya Vishnubhotla, Genta Indra Winata, Seid Muhie Yimam, Saif M. Mohammad
2023 J jnl
CoRR
Odunayo Ogundepo, Tajuddeen R. Gwadabe, Clara E. Rivera, Jonathan H. Clark, Sebastian Ruder, David Ifeoluwa Adelani, Bonaventure F. P. Dossou, Abdou Aziz Diop, Claytone Sikasote, Gilles Hacheme, Happy Buzaaba, Ignatius Ezeani, Rooweither Mabuya, Salomey Osei, Chris Emezue, Albert Njoroge Kahira, Shamsuddeen Hassan Muhammad, Akintunde Oladipo, Abraham Toluwase Owodunni, Atnafu Lambebo Tonja, Iyanuoluwa Shode, Akari Asai, Tunde Oluwaseyi Ajayi, Clemencia Siro, Steven Arthur, Mofetoluwa Adeyemi, Orevaoghene Ahia, Aremu Anuoluwapo, Oyinkansola Awosan, Chiamaka Chukwuneke, Bernard Opoku, Awokoya Ayodele, Verrah Otiende, Christine Mwase, Boyd Sinkala, Andre Niyongabo Rubungo, Daniel A. Ajisafe, Emeka Felix Onwuegbuzia, Habib Mbow, Emile Niyomutabazi, Eunice Mukonde, Falalu Ibrahim Lawan, Ibrahim Said Ahmad, Jesujoba O. Alabi, Martin Namukombo, Chinedu Emmanuel Mbonu, Mofya Phiri, Neo Putini, Ndumiso Mngoma, Priscilla A. Amuok, Ruqayya Nasir Iro, Sonia Adhiambo
2023 A* conf
EMNLP
Shamsuddeen Hassan Muhammad, Idris Abdulmumin, Abinew Ali Ayele, Nedjma Ousidhoum, David Ifeoluwa Adelani, Seid Muhie Yimam, Ibrahim Said Ahmad, Meriem Beloucif, Saif M. Mohammad, Sebastian Ruder, Oumaima Hourrane, Alípio Jorge, Pavel Brazdil, Felermino Dário Mário António Ali, Davis David, Salomey Osei, Bello Shehu Bello, Falalu Ibrahim Lawan, Tajuddeen Gwadabe, Samuel Rutunda, Tadesse Destaw Belay, Wendimu Baye Messelle, Hailu Beshada Balcha, Sisay Adugna Chala, Hagos Tesfahun Gebremichael, Bernard Opoku, Stephen Arthur
2023 J jnl
CoRR
Shamsuddeen Hassan Muhammad, Idris Abdulmumin, Abinew Ali Ayele, Nedjma Ousidhoum, David Ifeoluwa Adelani, Seid Muhie Yimam, Ibrahim Said Ahmad, Meriem Beloucif, Saif M. Mohammad, Sebastian Ruder, Oumaima Hourrane, Pavel Brazdil, Felermino Dário Mário António Ali, Davis David, Salomey Osei, Bello Shehu Bello, Falalu Ibrahim, Tajuddeen Gwadabe, Samuel Rutunda, Tadesse Destaw Belay, Wendimu Baye Messelle, Hailu Beshada Balcha, Sisay Adugna Chala, Hagos Tesfahun Gebremichael, Bernard Opoku, Steven Arthur
2023 conf
EMNLP (Findings)
Odunayo Ogundepo, Tajuddeen Gwadabe, Clara Rivera, Jonathan H. Clark, Sebastian Ruder, David Ifeoluwa Adelani, Bonaventure Dossou, Abdou Aziz Diop, Claytone Sikasote, Gilles Hacheme, Happy Buzaaba, Ignatius Ezeani, Rooweither Mabuya, Salomey Osei, Chris Emezue, Albert Kahira, Shamsuddeen Hassan Muhammad, Akintunde Oladipo, Abraham Toluwase Owodunni, Atnafu Lambebo Tonja, Iyanuoluwa Shode, Akari Asai, Aremu Anuoluwapo, Ayodele Awokoya, Bernard Opoku, Chiamaka Chukwuneke, Christine Mwase, Clemencia Siro, Stephen Arthur, Tunde Ajayi, Verrah Otiende, Andre Niyongabo Rubungo, Boyd Sinkala, Daniel A. Ajisafe, Emeka Onwuegbuzia, Falalu Ibrahim Lawan, Ibrahim Said Ahmad, Jesujoba O. Alabi, Chinedu E. Mbonu, Mofetoluwa Adeyemi, Mofya Phiri, Orevaoghene Ahia, Ruqayya Nasir Iro, Sonia Adhiambo
2023 conf
ACL (Findings)
Shantipriya Parida, Idris Abdulmumin, Shamsuddeen Hassan Muhammad, Aneesh Bose, Guneet Singh Kohli, Ibrahim Said Ahmad, Ketan Kotwal, Sayan Deb Sarkar, Ondrej Bojar, Habeebah A. Kakudi
2023 J jnl
CoRR
Shantipriya Parida, Idris Abdulmumin, Shamsuddeen Hassan Muhammad, Aneesh Bose, Guneet Singh Kohli, Ibrahim Said Ahmad, Ketan Kotwal, Sayan Deb Sarkar, Ondrej Bojar, Habeebah Adamu Kakudi
2023 J jnl
CoRR
Saminu Mohammad Aliyu, Idris Abdulmumin, Shamsuddeen Hassan Muhammad, Ibrahim Said Ahmad, Saheed Abdullahi Salahudeen, Aliyu Yusuf, Falalu Ibrahim Lawan
2023 conf
SemEval@ACL
Saminu Mohammad Aliyu, Idris Abdulmumin, Shamsuddeen Hassan Muhammad, Ibrahim Said Ahmad, Saheed Abdullahi Salahudeen, Aliyu Yusuf, Falalu Ibrahim Lawan
2023 J jnl
CoRR
Idris Abdulmumin, Auwal Abubakar Khalid, Shamsuddeen Hassan Muhammad, Ibrahim Said Ahmad, Lukman Jibril Aliyu, Babangida Sani, Bala Mairiga Abduljalil, Sani Ahmad Hassan
2023 conf
SemEval@ACL
Shamsuddeen Hassan Muhammad, Idris Abdulmumin, Seid Muhie Yimam, David Ifeoluwa Adelani, Ibrahim Said Ahmad, Nedjma Ousidhoum, Abinew Ali Ayele, Saif M. Mohammad, Meriem Beloucif, Sebastian Ruder
2023 J jnl
CoRR
Shamsuddeen Hassan Muhammad, Idris Abdulmumin, Seid Muhie Yimam, David Ifeoluwa Adelani, Ibrahim Said Ahmad, Nedjma Ousidhoum, Abinew Ali Ayele, Saif M. Mohammad, Meriem Beloucif, Sebastian Ruder
2022 J jnl
Microprocess. Microsystems
Muhammad Bashir Aliyu, Ahmed Amr, Ibrahim Said Ahmad
2022 J jnl
CoRR
Saminu Mohammad Aliyu, Gregory Maksha Wajiga, Muhammad Murtala, Shamsuddeen Hassan Muhammad, Idris Abdulmumin, Ibrahim Said Ahmad
2022 B conf
LREC
Idris Abdulmumin, Satya Ranjan Dash, Musa Abdullahi Dawud, Shantipriya Parida, Shamsuddeen Hassan Muhammad, Ibrahim Said Ahmad, Subhadarshi Panda, Ondrej Bojar, Bashir Shehu Galadanci, Bello Shehu Bello
2022 J jnl
CoRR
Idris Abdulmumin, Satya Ranjan Dash, Musa Abdullahi Dawud, Shantipriya Parida, Shamsuddeen Hassan Muhammad, Ibrahim Said Ahmad, Subhadarshi Panda, Ondrej Bojar, Bashir Shehu Galadanci, Bello Shehu Bello
2022 J jnl
CoRR
Shamsuddeen Hassan Muhammad, David Ifeoluwa Adelani, Sebastian Ruder, Ibrahim Said Ahmad, Idris Abdulmumin, Bello Shehu Bello, Monojit Choudhury, Chris Chinenye Emezue, Saheed Abdullahi Salahudeen, Aremu Anuoluwapo, Alípio Jeorge, Pavel Brazdil
2021 conf
ICCSA (9)
Idris Abdulmumin, Bashir Shehu Galadanci, Ibrahim Said Ahmad, Rabiu Ibrahim Abdullahi
2021 J jnl
IEEE Access
Atika Qazi, Glenn Hardaker, Ibrahim Said Ahmad, Mohammad Darwich, Jaafar Zubairu Maitama, Annie Dayani
2020 J jnl
IEEE Access
Abdollah Ansari, Ibrahim Said Ahmad, Azuraliza Abu Bakar, Mohd Ridzwan Yaakub
2020 J jnl
SN Comput. Sci.
Ibrahim Said Ahmad, Azuraliza Abu Bakar, Mohd Ridzwan Yaakub, Shamsuddeen Hassan Muhammad
2020 J jnl
Inf. Process. Manag.
Ibrahim Said Ahmad, Azuraliza Abu Bakar, Mohd Ridzwan Yaakub
2020 J jnl
Data Technol. Appl.
Ibrahim Said Ahmad, Azuraliza Abu Bakar, Mohd Ridzwan Yaakub, Mohammad Darwich
redb/extractors/decompiler/apk/analyzer.py
← Index redb/extractors/decompiler/apk/analyzer.py python
"""APK Code Analyzer — orchestrates androguard + JADX + apktool analysis.

This is the APK equivalent of BinaryNinjaDecompiler in
redb/extractors/decompiler/bninja/decompiler.py.
"""

import math
import os
import tempfile
from collections import Counter
from concurrent.futures import ThreadPoolExecutor, as_completed
from typing import Any, Dict, List, Optional, Set

from redb.extractors.decompiler.apk.apktool_wrapper import ApktoolDisassembler
from redb.extractors.decompiler.apk.jadx_wrapper import JADXDecompiler
from redb.extractors.decompiler.apk.library_filter import LibraryFilter
from redb.extractors.decompiler.apk.method_extractor import (
    compute_minhash,
    compute_prime_product_smali,
    compute_sha256,
    compute_ssdeep,
    compute_tlsh,
    count_call_instructions,
    dalvik_to_java_class,
    dalvik_to_java_prototype,
    detect_obfuscation_indicators,
)
from redb.extractors.decompiler.apk.smali_normalization import normalize_method_body
from redb.extractors.decompiler.apk.smali_cfg import compute_cfg_metrics
from redb.extractors.decompiler.apk.smali_parser import SmaliParser


class APKCodeAnalyzer:
    """Orchestrates APK code analysis combining three tools.

    Combines androguard (call graphs, xrefs, method enumeration),
    JADX (Java decompilation), and apktool (smali disassembly).
    """

    def __init__(
        self,
        filepath: str,
        timeout: int = 600,
        log=None,
        decompile_modules: Set[str] = None,
    ):
        self.filepath = filepath
        self.timeout = timeout
        self.log = log
        self.decompile_modules = decompile_modules or {"all"}
        self.min_instructions = int(
            os.getenv("APK_MIN_METHOD_INSTRUCTIONS", "5")
        )
        self.library_filter = LibraryFilter()
        self.jadx = JADXDecompiler(log=log)
        self.apktool = ApktoolDisassembler(log=log)

        self._temp_dirs = []

    def extract(self) -> Dict[str, Any]:
        """Run full APK code analysis.

        Returns dict with keys:
            decompiled_content, decompiled_refs,
            smali_content, smali_refs,
            similarity_metrics, strings, analysis_errors
        """
        results = {
            "decompiled_content": [],
            "decompiled_refs": [],
            "smali_content": [],
            "smali_refs": [],
            "similarity_metrics": [],
            "cfg": [],
            "strings": [],
            "analysis_errors": [],
        }

        # Create temp directories
        jadx_dir = tempfile.mkdtemp(prefix="redb_jadx_")
        apktool_dir = tempfile.mkdtemp(prefix="redb_apktool_")
        self._temp_dirs.extend([jadx_dir, apktool_dir])

        # Step 1-3: Run JADX, apktool, and androguard in parallel
        jadx_success = False
        apktool_success = False
        androguard_result = None

        with ThreadPoolExecutor(max_workers=3) as executor:
            futures = {}
            futures[executor.submit(self.jadx.decompile, self.filepath, jadx_dir)] = "jadx"
            futures[executor.submit(self.apktool.disassemble, self.filepath, apktool_dir)] = "apktool"
            futures[executor.submit(self._run_androguard)] = "androguard"

            for future in as_completed(futures):
                tool = futures[future]
                try:
                    result = future.result()
                    if tool == "jadx":
                        jadx_success = result
                    elif tool == "apktool":
                        apktool_success = result
                    elif tool == "androguard":
                        androguard_result = result
                except Exception as e:
                    if self.log:
                        self.log.error(f"{tool} failed: {e}")
                    results["analysis_errors"].append({
                        "class_name": None,
                        "method_name": None,
                        "error_location": tool,
                        "error_message": str(e),
                        "error_type": type(e).__name__,
                    })

        if androguard_result is None:
            if self.log:
                self.log.error("Androguard analysis failed — cannot proceed")
            return results

        apk_obj, dexs, analysis = androguard_result

        # Step 4b: Extract strings from DEX files
        try:
            results["strings"] = self._extract_strings(dexs)
        except Exception as e:
            if self.log:
                self.log.error(f"String extraction failed: {e}")
            results["analysis_errors"].append({
                "class_name": None,
                "method_name": None,
                "error_location": "strings",
                "error_message": f"String extraction failed: {e}",
                "error_type": type(e).__name__,
            })

        # Step 5: Parse smali output
        smali_methods = {}
        if apktool_success:
            try:
                smali_dirs = self.apktool.get_smali_directories(apktool_dir)
                for sdir in smali_dirs:
                    smali_methods.update(SmaliParser.parse_smali_directory(sdir))
            except Exception as e:
                if self.log:
                    self.log.error(f"Smali parsing failed: {e}")
                results["analysis_errors"].append({
                    "class_name": None,
                    "method_name": None,
                    "error_location": "apktool",
                    "error_message": f"Smali parsing failed: {e}",
                    "error_type": type(e).__name__,
                })

        # Step 6: Parse Java output
        java_methods = {}
        if jadx_success:
            try:
                java_methods = self.jadx.parse_java_methods(jadx_dir)
            except Exception as e:
                if self.log:
                    self.log.error(f"Java parsing failed: {e}")
                results["analysis_errors"].append({
                    "class_name": None,
                    "method_name": None,
                    "error_location": "jadx",
                    "error_message": f"Java parsing failed: {e}",
                    "error_type": type(e).__name__,
                })

        if not apktool_success and self.log:
            self.log.info(
                "apktool failed — falling back to androguard disassembly"
            )

        # Step 7-8: Process each method from androguard
        seen_decompiled_hashes = set()
        seen_smali_hashes = set()

        try:
            for method in analysis.get_methods():
                try:
                    self._process_method(
                        method,
                        smali_methods,
                        java_methods,
                        results,
                        seen_decompiled_hashes,
                        seen_smali_hashes,
                    )
                except Exception as e:
                    method_name = "unknown"
                    class_name = "unknown"
                    try:
                        if not method.is_external():
                            enc = method.get_method()
                            class_name = enc.get_class_name()
                            method_name = enc.get_name()
                    except Exception:
                        pass
                    results["analysis_errors"].append({
                        "class_name": class_name,
                        "method_name": method_name,
                        "error_location": "analysis",
                        "error_message": str(e),
                        "error_type": type(e).__name__,
                    })
        except Exception as e:
            if self.log:
                self.log.error(f"Method enumeration failed: {e}")
            results["analysis_errors"].append({
                "class_name": None,
                "method_name": None,
                "error_location": "androguard",
                "error_message": f"Method enumeration failed: {e}",
                "error_type": type(e).__name__,
            })

        if self.log:
            stats = self.library_filter.get_filter_stats()
            self.log.info(
                f"APK analysis complete: {stats['user']} user methods, "
                f"{stats['library']} library methods filtered, "
                f"{len(results['decompiled_content'])} decompiled, "
                f"{len(results['smali_content'])} smali, "
                f"{len(results['strings'])} strings"
            )

        return results

    def _run_androguard(self):
        """Run androguard analysis on the APK."""
        from androguard.misc import AnalyzeAPK
        return AnalyzeAPK(self.filepath)

    def _process_method(
        self,
        method,
        smali_methods: Dict,
        java_methods: Dict,
        results: Dict,
        seen_decompiled: set,
        seen_smali: set,
    ):
        """Process a single method from androguard analysis."""
        # Skip external methods (no code body)
        if method.is_external():
            return

        encoded = method.get_method()
        class_name = encoded.get_class_name()
        method_name = encoded.get_name()
        descriptor = encoded.get_descriptor()

        # Build method key for cross-tool matching
        method_key = SmaliParser.make_method_key(class_name, method_name, descriptor)

        # Check if library
        is_lib = self.library_filter.is_library(class_name)
        method_type = "LIBRARY" if is_lib else "USER"

        # Skip library methods for content tables (but they're still in xrefs)
        if is_lib:
            return

        # Get xrefs
        # In androguard 4.x, xref tuples are (ClassAnalysis, MethodAnalysis, offset).
        # The MethodAnalysis wrapper doesn't expose get_class_name()/get_name()
        # directly — we need to unwrap via .get_method() first.
        callers = []
        callees = []
        try:
            for ref_class, ref_method, offset in method.get_xref_from():
                try:
                    enc = ref_method.get_method()
                    caller_key = f"{enc.get_class_name()}->{enc.get_name()}"
                except AttributeError:
                    # Fallback for older androguard where ref_method is EncodedMethod
                    caller_key = f"{ref_method.get_class_name()}->{ref_method.get_name()}"
                callers.append(caller_key)
        except Exception:
            pass

        try:
            for ref_class, ref_method, offset in method.get_xref_to():
                try:
                    enc = ref_method.get_method()
                    callee_key = f"{enc.get_class_name()}->{enc.get_name()}"
                except AttributeError:
                    callee_key = f"{ref_method.get_class_name()}->{ref_method.get_name()}"
                callees.append(callee_key)
        except Exception:
            pass

        # Look up smali body
        smali_method = smali_methods.get(method_key)
        smali_body = None
        smali_normalized = None
        smali_hash = None
        instruction_count = 0
        register_count = 0

        if smali_method:
            smali_body = smali_method.body
            instruction_count = smali_method.instruction_count
            register_count = smali_method.register_count
        else:
            # Fallback: use androguard's own disassembler
            smali_body, instruction_count, register_count = (
                self._disassemble_with_androguard(encoded)
            )

        # Apply minimum instruction filter
        if instruction_count < self.min_instructions:
            return

        if smali_body:
            smali_normalized = SmaliParser.normalize_smali_body(smali_body)
            if smali_normalized:
                smali_hash = compute_sha256(smali_normalized)

        if not smali_hash:
            return

        # Compute obfuscation indicators from smali
        obfuscation = detect_obfuscation_indicators(
            method_name, class_name, smali_body, instruction_count
        )

        # Compute CFG metrics early — needed for both content tables and cfg table
        cfg_metrics = None
        if smali_body:
            try:
                cfg_metrics = compute_cfg_metrics(smali_body)
            except Exception:
                pass

        # Smali content (deduplicated)
        if smali_hash and smali_hash not in seen_smali:
            seen_smali.add(smali_hash)
            results["smali_content"].append({
                "smali_method_hash": smali_hash,
                "smali_method": smali_body,
                "smali_method_type": method_type,
                "smali_instructions_count": instruction_count,
                "smali_register_count": register_count,
                "smali_has_string_encryption": obfuscation.get("has_string_encryption", False),
                "smali_has_reflection_calls": obfuscation.get("has_reflection_calls", False),
                "smali_excessive_goto_count": obfuscation.get("excessive_goto_count", False),
                "smali_flattened_score": cfg_metrics.flattened_score if cfg_metrics else 0.0,
                "smali_mba_score": cfg_metrics.mba_score if cfg_metrics else 0.0,
            })

        # Smali reference (per-binary)
        ssdeep_val = compute_ssdeep(smali_normalized) if smali_normalized else None
        tlsh_val = compute_tlsh(smali_normalized) if smali_normalized else None

        # Semantically normalized ssdeep/TLSH (analogous to Binja's ssdeep_llil/tlsh_llil)
        ssdeep_normalized_val = None
        tlsh_normalized_val = None
        if smali_normalized:
            normalized_lines = normalize_method_body(smali_normalized, level="opcode_api")
            if normalized_lines:
                normalized_text = "\n".join(normalized_lines)
                ssdeep_normalized_val = compute_ssdeep(normalized_text)
                tlsh_normalized_val = compute_tlsh(normalized_text)

        # Look up Java source
        java_class = dalvik_to_java_class(class_name)
        java_key_candidates = [
            f"{java_class}.{method_name}",
        ]
        java_source = None
        decompiled_hash = None

        for jk in java_key_candidates:
            for key, source in java_methods.items():
                if key.startswith(jk):
                    java_source = source
                    break
            if java_source:
                break

        if java_source:
            # Normalize and hash decompiled content
            normalized_java = _normalize_java(java_source)
            decompiled_hash = compute_sha256(normalized_java)

            # Decompiled content (deduplicated)
            if decompiled_hash not in seen_decompiled:
                seen_decompiled.add(decompiled_hash)
                results["decompiled_content"].append({
                    "decompiled_method_hash": decompiled_hash,
                    "decompiled_method": java_source,
                    "decompiled_method_type": method_type,
                    "decompiled_has_string_encryption": obfuscation.get("has_string_encryption", False),
                    "decompiled_has_reflection_calls": obfuscation.get("has_reflection_calls", False),
                    "decompiled_excessive_goto_count": obfuscation.get("excessive_goto_count", False),
                })

            # Decompiled reference
            method_prototype = dalvik_to_java_prototype(
                method_name, descriptor, class_name
            )
            results["decompiled_refs"].append({
                "decompiled_method_hash": decompiled_hash,
                "smali_method_hash": smali_hash,
                "decompiled_class_name": dalvik_to_java_class(class_name),
                "decompiled_method_name": method_name,
                "decompiled_method_signature": descriptor,
                "decompiled_method_prototype": method_prototype,
                "functions_caller": callers,
                "functions_call": callees,
            })

        # Smali reference
        if smali_hash:
            results["smali_refs"].append({
                "smali_method_hash": smali_hash,
                "decompiled_method_hash": decompiled_hash,
                "smali_class_name": dalvik_to_java_class(class_name),
                "smali_method_name": method_name,
                "smali_method_signature": descriptor,
                "ssdeep_smali": ssdeep_val,
                "tlsh_smali": tlsh_val,
            })

        # Similarity metrics (content-based fuzzy matching only)
        if smali_hash and smali_normalized:
            minhash_sig = compute_minhash(smali_normalized)
            sim_entry = {
                "smali_method_hash": smali_hash,
                "cyclomatic_complexity": cfg_metrics.cyclomatic_complexity if cfg_metrics else None,
                "ssdeep_smali": ssdeep_val,
                "tlsh_smali": tlsh_val,
                "ssdeep_smali_normalized": ssdeep_normalized_val,
                "tlsh_smali_normalized": tlsh_normalized_val,
                "minhash": minhash_sig or [],
            }
            results["similarity_metrics"].append(sim_entry)

        # CFG entry (structural/topological features)
        if smali_hash and cfg_metrics and smali_body:
            prime_product = compute_prime_product_smali(smali_body)
            call_count = count_call_instructions(smali_body)
            cfg_entry = {
                "smali_method_hash": smali_hash,
                "cfg_topology_hash": cfg_metrics.cfg_topology_hash,
                "block_count": cfg_metrics.block_count,
                "edge_count": cfg_metrics.edge_count,
                "cfg_instructions_count": instruction_count,
                "call_count": call_count,
                "cyclomatic_complexity": cfg_metrics.cyclomatic_complexity,
                "loop_count": cfg_metrics.loop_count,
                "max_depth": cfg_metrics.max_depth,
                "max_fan_out": cfg_metrics.max_fan_out,
                "md_index_topdown": cfg_metrics.md_index_topdown,
                "md_index_bottomup": cfg_metrics.md_index_bottomup,
                "prime_product_smali": prime_product,
                "cfg_feature_tlsh": cfg_metrics.cfg_feature_tlsh,
                "wl_minhash": cfg_metrics.wl_minhash,
                "bb_features": cfg_metrics.block_features,
                "cfg_adjacency": cfg_metrics.cfg_adjacency,
            }
            results["cfg"].append(cfg_entry)

    @staticmethod
    def _string_entropy(s: str) -> float:
        """Compute Shannon entropy of a string."""
        if not s:
            return 0.0
        freq = Counter(s)
        length = len(s)
        return -sum(
            (count / length) * math.log2(count / length)
            for count in freq.values()
        )

    def _extract_strings(self, dexs) -> List[Dict[str, Any]]:
        """Extract deduplicated strings from all DEX objects.

        Uses androguard's get_strings() on each DEX, deduplicates by value,
        and computes entropy — matching the Binja StringAnalysis output format
        so IOCExtractorFromResults can consume them identically.

        Returns list of dicts with keys:
            string, string_encoding, string_offset, string_length, string_entropy
        """
        seen = set()
        strings = []
        offset_counter = 0

        for dex in dexs:
            try:
                dex_strings = dex.get_strings()
            except Exception:
                continue

            if not dex_strings:
                continue

            for s in dex_strings:
                if not s or s in seen:
                    continue
                # DEX MUTF-8 strings may contain unpaired surrogates (e.g. \ud800)
                # that are invalid UTF-8 and will fail ClickHouse insert
                s = s.encode('utf-8', errors='replace').decode('utf-8')
                if not s or s in seen:
                    continue
                seen.add(s)

                strings.append({
                    "string": s,
                    "string_encoding": "UTF8",
                    "string_offset": offset_counter,
                    "string_length": len(s),
                    "string_entropy": self._string_entropy(s),
                })
                offset_counter += 1

        return strings

    @staticmethod
    def _disassemble_with_androguard(encoded):
        """Fallback disassembly using androguard when apktool fails.

        Returns (smali_body, instruction_count, register_count).
        """
        code = encoded.get_code()
        if not code:
            return None, 0, 0

        register_count = code.get_registers_size()
        lines = []
        instruction_count = 0

        try:
            bytecode = code.get_bc()
            if not bytecode:
                return None, 0, register_count

            for instruction in bytecode.get_instructions():
                op_name = instruction.get_name()
                output = instruction.get_output()
                if output:
                    output = _normalize_androguard_operands(op_name, output)
                    lines.append(f"    {op_name} {output}")
                else:
                    lines.append(f"    {op_name}")
                instruction_count += 1
        except Exception:
            return None, 0, register_count

        if not lines:
            return None, 0, register_count

        smali_body = "\n".join(lines)
        return smali_body, instruction_count, register_count

    def cleanup(self):
        """Remove temporary directories."""
        import shutil
        for d in self._temp_dirs:
            try:
                if os.path.isdir(d):
                    shutil.rmtree(d)
            except Exception:
                pass
        self._temp_dirs.clear()


def _normalize_androguard_operands(op_name: str, output: str) -> str:
    """Normalize androguard instruction output to match apktool smali format.

    Androguard's get_output() differs from apktool in two key ways:
    1. invoke-* operands lack {braces} around registers:
       androguard: 'v0, v1, Lcom/Foo;->bar()V'
       apktool:    '{v0, v1}, Lcom/Foo;->bar()V'
    2. Field instructions use a space instead of colon between name and type:
       androguard: 'v0, v1, LA;->field Ljava/lang/String;'
       apktool:    'v0, v1, LA;->field:Ljava/lang/String;'
    """
    # invoke-* instructions: wrap register args in {braces}
    if op_name.startswith("invoke-"):
        # Find the class/method reference (starts with L or [)
        # Split on ', ' and find where the reference begins
        parts = output.split(", ")
        reg_parts = []
        ref_idx = None
        for i, part in enumerate(parts):
            stripped = part.strip()
            if stripped.startswith("L") or stripped.startswith("["):
                ref_idx = i
                break
            reg_parts.append(part)

        if ref_idx is not None and reg_parts:
            regs = ", ".join(reg_parts)
            ref = ", ".join(parts[ref_idx:])
            return "{" + regs + "}, " + ref
        elif reg_parts:
            # No reference found — just wrap all as registers
            return "{" + ", ".join(reg_parts) + "}"

    # Field instructions: fix 'field Ltype;' → 'field:Ltype;'
    if op_name.startswith(("iget", "iput", "sget", "sput")):
        # Pattern: '... ClassName;->fieldName Ltype;' or '... ClassName;->fieldName [Ltype;'
        # The space between fieldName and the type descriptor should be a colon
        arrow_idx = output.find("->")
        if arrow_idx != -1:
            after_arrow = output[arrow_idx + 2:]
            # Find the space before the type descriptor
            space_idx = after_arrow.find(" ")
            if space_idx != -1:
                remaining = after_arrow[space_idx + 1:]
                # Check that what follows is a type descriptor
                if remaining.startswith(("L", "[", "Z", "B", "S", "C",
                                         "I", "J", "F", "D")):
                    after_arrow = after_arrow[:space_idx] + ":" + remaining
                    output = output[:arrow_idx + 2] + after_arrow

    return output


def _normalize_java(source: str) -> str:
    """Normalize Java source for consistent hashing.

    Strip leading/trailing whitespace, normalize indentation.
    """
    lines = []
    for line in source.split("\n"):
        stripped = line.strip()
        if stripped:
            lines.append(stripped)
    return "\n".join(lines)